Skip to main content

Inside C#

·        An abstraction refers to how a given problem is represented in the program space. Object-oriented languages enable you to declare classes whose names and interfaces closely mimic real-world problem domain entities such that using the objects have a more natural "feel" to them.

 

·        Inheritance relates to the programmer's ability to specify that one class have a kind-of relationship with another class. A derived class should require no more and promise no less than its base class on any inherited interfaces. When a programmer has a reference to a derived class, the programmer can always treat that class as though it is the base class. This is called upcasting.

 

·        Managed code is just code that is running under the guidance of the CLR and is therefore being managed by the CLR. In a managed code environment, a number of rules are in place to ensure that all applications behave in a globally uniform manner, regardless of the language they were written in.

 

·        To make it easy for language writers to port their languages to .NET, Microsoft developed a language akin to assembly language called Microsoft intermediate language (MSIL). To compile applications for .NET, compilers take source code as input and produce MSIL as output. MSIL itself is a complete language that you can write applications in.

 

·        In a .Net PE file (Assembly), the operating system can't execute the MSIL code, so from the entry point, it jumps to the _ CorExeMain function in mscoree.dll. The _ CorExeMain function starts the execution of the MSIL code that was placed in the PE.

 

·        Three different JITters can be used to convert the MSIL into native code, depending on the circumstances:

 

1. Install-time code generation: It will compile an entire assembly into CPU-specific binary code, just as a C++ compiler does. An assembly is the code package that's sent to the compiler.

2. JIT: The default JITter is called at run time -each time a method is invoked for the first time.

 

3. EconoJIT: Another run-time JITter, the EconoJIT is specifically designed for systems that have limited resources—for example, handheld devices with small amounts of memory. It uses code pitching. Code pitching allows the EconoJIT to discard the generated, or compiled, code if the system begins to run out of memory. The benefit is that the memory is reclaimed. However, the disadvantage is that if the code being pitched is invoked again, it must be compiled again as though it had never been called.

 

·        In addition to mapping source code to MSIL instruction sequences, CLS-compliant compilers have another equally important task: embedding metadata into the resulting EXE.

 

·        The means by which the metadata of an assembly is queried is called reflection. Tools such as Visual Studio.NET use these reflection methods to implement features such as IntelliSense. Another incredibly useful .NET tool that takes advantage of reflection is the Microsoft .NET Framework IL Disassembler (ILDASM).

 

·        Unmanaged code is code that is not controlled by the .NET runtime. This code is still run by the .NET runtime. However, unmanaged code doesn't have the advantages that managed code has, such as garbage collection, a unified type system, and metadata. When you want to call COM components by managed code, you do this by creating a .NET wrapper for the COM component so that the managed client thinks it's working with a .NET class. And when a COM component wants to call a .NET component, it's solved using a reciprocal approach: a COM client is fooled into thinking it's using a COM server, which is actually a .NET service of some sort.

 

·        Creating Alias For The Classes In A Name Space

 

using output = System.Console;

 

output.WriteLine("Hello, World");

 

·        Viewing Assembly Code (MSIL) Of An Exe/DLL

 

In the Start menu's Run dialog box, type ildasm and click OK. You'll see a nondescript application with a few menu options. At this point, from the File menu, click Open and specify the .Net assembly (Exe/DLL). To tell whether an EXE or DLL is managed, attempt to open it with ILDASM. If the file is a valid managed file, it will be opened. If it's not, you'll receive an error message stating that <your file> has no valid CLR header and cannot be disassembled

 

·        Hungarian Notation: Hungarian notation specifies that a prefix be added to each variable to indicate its type.

 

·        CTS is responsible for defining the types that can be used across .NET languages. The CTS separate types into two categories. Value types (Primitive type) and reference types. Variables of value types are stored in the stake and variables of reference types are stored on the heap. Value types variables cannot be null but the reference types variables can be. Reference types are type-safe pointers. All types are ultimately derived from System.Object type.

·        Boxing is the conversion of a value type to a reference type. Unboxing is the conversion of a reference type to a value type.

 

      int foo = 42;         // Value type.
      object bar = foo;     // foo is boxed to bar.
      int foo2 = (int)bar;  // Unboxed back to int.

 

When converting from a value type to a reference type—there is no explicit cast needed. However, when unboxing—the cast is needed. This is because in the case of unboxing, an object could be cast to any type. Therefore, the cast is necessary so that the compiler can verify that the cast is valid per the specified variable type.

 

·        CTS Types and Aliases

 

CTS Type Name

C# Alias

Description

System.Object

object

Base class for all CTS types

System.String

string

String

System.SByte

sbyte

Signed 8-bit byte (1 byte)

System.Byte

byte

Unsigned 8-bit byte (1 byte)

System.Int16

short

Signed 16-bit value (2 bytes)

System.UInt16

ushort

Unsigned 16-bit value (2 bytes)

System.Int32

int

Signed 32-bit value (4 bytes)

System.UInt32

uint

Unsigned 32-bit value (4 bytes)

System.Int64

long

Signed 64-bit value (8 bytes)

System.UInt64

ulong

Unsigned 64-bit value (8 bytes)

System.Char

char

16-bit Unicode character (2 bytes)

System.Single

float

IEEE 32-bit float (4 bytes)

System.Double

double

IEEE 64-bit float (8 bytes)

System.Boolean

bool

Boolean value (true/false)

System.Decimal

decimal

128-bit data type exact to 28 or 29 digits—mainly used for financial applications where a great degree of accuracy is required. (16 bytes)

 

·        Upcasting (where a derived class object is assigned to a base class object) is implicit and legal in C# but the reverse or downcasting is not implicit and illegal in C#. If you try to downcast the objects explicitly, no compilation error occurs, but a run-time exception system.InvalidCastException would be there. If you downcast the objects using 'As' keyword like : <DerivedClass> c = e as <DerivedClass>, where e is a object of base class, no compilation or run-time error would be there, instead object c would be assign to null in case of invalid casting.

 

·        C# Access Modifiers

 

Public

Member is accessible from outside the class's definition and hierarchy of derived classes.

Protected

The member is not visible outside the class and can be accessed by derived classes only.

Private

The member cannot be accessed outside the scope of the defining class.

Internal

Member is accessible within the current assembly.

 

·        Command-Line Parameters

 

      public static void Main(string[] args)
      {
 
      }

   

 

·        C# included a mechanism by which you can define more than one class with a Main method. To specify which Main() method to call at compilation you can use [csc MultipleMain.cs /main:<ClassName>] switch.
 
·        Calling Base Class Constructor
 
      class B : A
      {
          public B() : base()
          {
          }
      }
 
·        Constants: First, a constant is a member whose value is set at compilation time, either by the programmer or defaulted by the compiler. Second, a constant member's value must be written as a literal. By default const members are static.
 
      public const double pi = 3.14;
 
·        Read-Only Fields: When you define a field with the readonly keyword, you have the ability to set that field's value in one place: the constructor. The read-only fields that we defined are instance fields.
 
        public readonly int ScreenWidth;
    
·        Static Constructors: Static constructors are constructors that are used to initialize static fields, read-only or otherwise.
 
·        In case of inheritance, the base class's constructor cannot be inherited. Each class must implement its own constructor irrespective of its base class.

 

·        An abstract class cannot be used as a sealed class.

 

·        Passing Parameters By Reference (Like C++ Pointers)

 

Function Definition:

 

  public void GetName(ref int age)

  {

}

 

Function Calling:

 

{

       int age;

     Object.GetName(ref age);

}

 

When you use the ref keyword, you must initialize the passed arguments before calling the method.

 

Out Keyword: Use 'Out' keyword in place of 'ref' and you would not required to initialize the passed arguments before calling the method.

 

·        In case of overloading, it's not sufficient to have only return type different, but method's argument list must be different.

 

·        Variable Method Parameters

 

public void GetName(params string[] s)

     {

}

 

     Function Calling:

 

     {

          string a;

          string b;

          Object.GetName(a,b);

     }

    

use GetLength() method of passed param array to find out number of elements sent.

 

·        Overriding: Use 'new' keyword in derived class method when you are going to override the base class method.

 

·        Polymorphism In Overriding: To implement late binding in method calling, we use 'virtual' keyword in base class's method and 'override' keyword in derived class's method. Virtual methods cannot be declared as private because, by definition, they would not be visible in the derived classes.

 

·        When you have an object of a class, which has a static method, even though you cannot call the method by object. You still have to call that method by class name (ClassName.MethodName() not like ObjectName.Method()).

 

·        A static method can access any static member within the class, but it cannot access an instance member.

 

·        Value Variable In Properties: Even though you do not declare a variable named 'value' in code, but you can still use that variable in properties set segment, because compiler injects this variable as an argument of a method in IL code, which represents your set segment of the property.

 

·        Derived class can inherit and override properties just as it could any other member from the base class. But, if the base class' property contains both get and set segment, you have to implement both these segment in your derived class version.

 

·        Lazy Initialization: Properties enable a class to guarantee that any additional processing can be done when a particular field is modified or accessed. This feature of the properties can be used in the implementation of something called lazy initialization. This is an optimization technique whereby some of a class's members are not initialized until they are needed, whose initialization takes up a lot of time or chews up a lot of resources.

 

·        Arrays

 

int[] numbers;

numbers = new int[6];

 

When declaring the array as a member of a class, you need to declare and instantiate the array in two distinct steps because you can't instantiate an object until run time. (Arrays are object of System.Array class)

 

Length() method of an array object return total number of elements that array contains. Whereas GetLenght() return length or upper bound of the each dimension.

 

The number of dimensions in an array is called an array's rank, and rank is retrieved using the Array.Rank property.

 

A jagged array is simply an array of arrays.

 

int[][] jaggedArray;
 
   jaggedArray = new int[2][];
 

 jaggedArray[0] = new int[3]

 

·        Indexers: A C#-specific feature called indexers enables you to programmatically treat objects as though they were arrays.

 

·        Interface: In terms of C++, an interface is basically an abstract class with only pure virtual methods. All interface methods are public by definition.

 

·        Is Operator: The 'is' operator enables you to check at run time whether one type is compatible with another type.

 

if (myControl is ISerializable)

     {} else {}

 

·        As Operator: As operator works same as 'is' operator, but in slightly different manner. It not only checks for compatibility between two types but also try to convert between. If two types are not compatible with each other, then it assigns a null value to target type object.

 

     ClsMyClass1 Obj1 = new ClsMyClass1();

     ClsMyClass2 Obj2 = new ClsMyClass2();

 

     ClsMyClass2 Obj2 = Obj1 as ClsMyClass2;

 

If Obj1 and Obj2 were compatible in types then casting would be done, unless Obj2 will contains a null value.

 

·        When a class implements an interface, by default all the implemented methods are visible out side the class, because those methods are declared public in the interface. If you want to hide those methods you have to remove the access modifier 'public' in class implementation and qualify the members name with interface name.

 

     public interface IDataBound

     {void Bind();}

 

     public class EditBox : IDataBound

     {

void IDataBound.Bind()

{}

}

 

public static main()

{

     EditBox edit = new EditBox();

     edit.Bind();  // This will cause an error.

 

     IDataBound bound = (IDataBound) edit;

     Bound.Bind(); // This will run.

}

 

This is also used to solve the ambiguity between more then one interfaces, a class is implementing. For example, a class is implementing more then one interfaces and each is providing the a method with same name, then you can implement each method by its own interface qualifier name and at run time you can cast the class object to that particular interface, who's method you want to call. So, always cast the object to the interface whose member you're attempting to use.

 

·        Two common problems are associated with interfaces and inheritance:

 

1)      Consider the first problem

1)      You have a base class with a method named Foo().

2)      You have an interface with a method named Foo().

3)      Now you derived a class both from base class and interface, and you do not implements method Foo() of the interface in that derived class.

4)      When you make an object of that derived class and try to call that method Foo(), it will compile and run without any error (despite the fact that you have to implements all the methods of an interface in a implementing class). Because when compiler searches the Foo() method for the interface, it founds that base class method Foo() as implemented and code works. But it does not give desirable results. And when you cast the derived class object to the interface type and try to call Foo(), you would probably get the error.

 

2)      Consider the second problem

1)      You have an interface with a method Foo().

2)      You have a base class which implements that interface and its methods (i.e. Foo())

3)      You have a class derived from that base class, and it also have its own method named Foo() (Declared with 'new' keyword, overriding base class method).

4) Now when you try to call Foo() method by the derived class object, which method will be called ? It depends on what reference you have. If you have a reference to the derived class object, its Foo() method will be called. However, when you explicitly cast the derived class object to the interface, the implemented method of the interface will be called (base class method).

·        You can combine the two or more interfaces in one, so that a class, which is implementing the combined interface, also implements all the methods of other interfaces as well.

     public interface IA

     public interface IB

     public interface IX : IA, IB   // Combining interfaces

public class MyClass : IX      // Implementing all three interfaces

C# Operators:

(), x.y, a[x], ++, --, new, typeof, sizeof (Works on only value types), checked, unchecked, <operator>= (i.e. +=, -=), >, <, >=, <=, ==, !=,

·        Member-By-Member Comparison Of Two Objects:

     Object1.Equals(Object2) 

  It returns true if both object belongs to same class or have same members. When you compare two objects like (Object1 == Object2) you will get a false value. Because C# compare objects by their memory addresses and that is different for any two objects.

·        When you assign one object to another, basically you are not copying one object to another. What actually you are doing is that you are making same memory reference for both of the objects. So that if you make change in one object, it will be reflected in another one also.

For example:

    

     MyClass Obj1 = new MyClass();

     MyClass Obj2 = new MyClass();

    

     Obj1 = Obj2;

    

Now both objects are pointing the same memory location. And the object Obj1 is destroyed and eventually collected by GC.

 

It also happens when you pass the object to a method. When this is the case the object's reference is actually passes to method, so that any changes (in value of members) in the passed object in the method code also reflect in the actual object in calling program.

 

·        The continue statement stops the current iteration and returns control back to the top of the loop for the next iteration.

 

·        If the return statement is a try block that contains an associated finally block, control is actually passed to the first line of the finally block, and when that block of code finishes, control is passed back to the caller.

 

·        A Constructor cannot returns value.

 

·        If you put more then one catch block handling different exceptions, you have to put those block in a order that all the derived exception (from System.Exception) come first, then at last the type of base class (System.Exception) exception. If not so, then the other derived exceptions would be unreachable.

 

·        Operator Overloading:

 

Generic syntax is:

public static retval operator op ( object1 [, object2 ])

 

All overloaded operator methods must be defined as public and static. The number of arguments passed (object1, object2) depends on the type of operator being overloaded.

If the operator being overloaded is a binary operator, the first argument must be the same type as that of the enclosing class and the second argument can be any type.

Only the following unary and binary operators can be overloaded.

Unary operands: +, -, !, ~, ++, --, true, false

Binary operands: +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=

When you overload a binary operator, its compound assignment equivalent is implicitly overloaded. For example, if you overload the + operator, the += operator is implicitly overloaded in that the user-defined operator+ method will be called. Also, an operator's syntax can't be modified. You can't change the binary * operator to take three arguments when, by definition, its syntax calls for two operands.

 

·        User-Defined Conversions:

 

Generic syntax is:

public static implicit/explicit operator conv-type-out (conv-type-in operand)

 

The implicit keyword means that the cast is not required by the client and will occur automatically. Conversely, using the explicit keyword signifies that the client must explicitly cast the value to the desired type.

 

·        Delegates:

 

Callback methods are used when you need to pass a function pointer to another function that will then call you back (via the passed pointer).

 

·        Events:

 

Events in C# follow the publish-subscribe design pattern in which a class publishes an event that it can "raise" and any number of classes can then subscribe to that event. Once the event is raised, the runtime takes care of notifying each subscriber that the event has occurred. The method called; as a result of an event being raised is defined by a delegate. The delegate must be defined as taking two arguments. These arguments always represent two objects: the object that raised the event (the publisher) and an event information object. Additionally, this second object must be derived from the .NET Framework's EventArgs class.

 

·        Threading:

 

A thread is a unit of processing, and multitasking is the simultaneous execution of multiple threads. Multitasking comes in two flavors: cooperative and preemptive. In case of cooperative multitasking, each thread was responsible for relinquishing control to the processor so that it could process other threads. With preemptive multitasking, the processor is responsible for giving each thread a certain amount of time in which to execute — a time-slice. .NET only work on preemptive multitasking operating systems. In .NET, threads run in something called an AppDomain. In .NET, threads can cross AppDomain boundaries, and a method in one thread can call a method in another AppDomain. Therefore, here's a better definition of an AppDomain: a logical process inside of a physical process.

 

·        Context Switching:

 

When the hardware timer signals the interrupt, the processor saves all registers for the current thread onto the stack. Then the processor moves those same registers from the stack into a data structure called a CONTEXT structure. When the processor wants to switch back to a previously executing thread, it reverses this procedure and restores the registers from the CONTEXT structure associated with the thread. This entire procedure is called context switching.

 

·        You pause or suspend a thread by Thread.Sleep(Milliseconds) static method of the thread class. You're not allowed to call Thread.Sleep on any other thread except the currently executing one. The second way to suspend the execution of a thread is by using the Thread.Suspend method. There is some major difference between the two techniques. First, the Thread.Suspend method can be called on the currently executing thread or another thread. Second, once a thread is suspended in this fashion, only another thread can cause its resumption, with the Thread.Resume method. You can destroy a thread using it's objects Abort() method. You can set a thread's priority by Thread.Priority method. The default priority is Normal.

 

·        Through synchronization, you specify critical sections of code that can be entered by only one thread at a time, thereby guaranteeing that any temporary invalid states of your object are not seen by the object's clients. We achieve the synchronization between threads by Monitor class or Mutex Class.

 

·        Reflection:

 

o       .NET allows you to write code to access an application's metadata through a process known as reflection. Reflection is the ability to discover type information at run time. The reflection API let's you do such things as iterate an assembly's modules, iterate an assembly's types, and retrieve the different design-time characteristics of a type. Advanced reflection tasks include using reflection to dynamically invoke methods and use types (via late binding) and even to create and execute MSIL code at run time. We have to use following namespaces to works the code:

               using System;
            using System.Diagnostics;
            using System.Reflection;

 

o       Retrieving the type of an instance:

  int i = 6;

  Type t = i.GetType()

 

o       Retrieving the type from a name:

        Type t = Type.GetType("System.Int32");

 

(Note that you cannot use the C# aliases when calling the Type.GetType method.)

 

o       You can interrogate about a type by first creating a object of that type and then using its methods, which all start from prefix 'Is' (i.e. type.IsByRef).

 

o       Getting all the types an assembly contains:

 

Assembly a = Assembly.LoadFrom("MyApp.Exe");

Type[] types = a.GetTypes();
     
foreach(Type t in types)
        {t.FullName;}

       

o       To get all the modules an assembly contains:

 

Assembly a = Assembly.LoadFrom("MyApp.Exe");

Module[] modules = a.GetModules();
     
foreach(Module m in modules)
        {m.Name;}
 
o       Creating types at run time involves using the System.Reflection.Emit namespace. Using the classes in this namespace, you can define an assembly in memory, create a module for an assembly, define new types for a module (including its members), and even emit the MSIL opcodes for the application's logic.

 

·        Unmanaged code refers to code that is not managed, or controlled, by the .NET runtime.

 

·        Platform Invocation Services:

 

o       The .NET Platform Invocation Services—sometimes referred to as PInvoke—allows managed code to work with functions and structures that have been exported from DLLs.

Attributes are used to provide design-time information for a C# type. Through reflection, this information can later be queried at run time. C# makes use of an attribute to allow you to describe the DLL function that the application will call to the compiler. This attribute is called the DllImport attribute, and its syntax is shown here:

        using System.Runtime.InteropServices;
 
      [DllImport("DllName")]
      accessModifier static extern retValue dllFunction (    param1, param2,...);

 

o       Not only can a C# application call a DLL function, but the DLL function can also call designated C# methods in your application. You do it by using function callbac.

 

o       Marshalling: Any time you call a DLL function, .NET has to marshal the parameters to that function and the return value back to the calling .NET application. It happens almost implicitly because .NET has a defined default native type for each .NET type.

 

o       An unmanaged code is a code for which the .NET runtime will not be controlling the allocation and de-allocation of memory. You write unsafe code by using two keywords: unsafe and fixed. The unsafe keyword specifies that the marked block will run in an unmanaged context. The fixed keyword is responsible for the pinning of managed objects. Pinning is the act of specifying to the garbage collector (GC) that the object in question cannot be moved. As it happens, during the execution of an application, objects are allocated and de-allocated and "spaces" in memory open up. Instead of memory becoming fragmented, the .NET runtime moves the objects around to make the most efficient use of memory.

 

o       Pointers in C# can be acquired only for value types, arrays, and strings. Pointers operators in c# are similar to C/C++:

 

Operator

Description

&

The address-of operator returns a pointer that represents the memory address of the variable.

*

The de-reference operator is used to denote the value pointed at by the pointer.

->

The de-referencing and member access operator is used for member access and pointer de-referencing.

 

·        COM Interoperability:

A .NET application that needs to talk to a COM component cannot directly consume the functionality that's exposed by that component, because the .NET runtime is designed to work with components that have metadata, whereas COM is designed to work through the Registry and a series of interrogatory methods that are implemented by the component. Therefore, we need to enable a COM component to generate some metadata for it. In the case of a COM component, this metadata layer is used by the runtime to determine type information. This type information is then used at run time to manufacture what's called a runtime callable wrapper (RCW). The RCW handles the actual activation of the COM object and handles the marshalling requirements when the .NET application interacts with it. The RCW also does tons of other chores, such as managing object identity, object lifetimes, and interface caching. The RCW serves the purpose of giving the .NET application the notion that it's interacting with a managed .NET component, and it gives the COM component in the unmanaged space the impression that a traditional COM client is calling it. The TLBIMP utility is used to read and generate metadata of a COM component. It read the COM type-lib and generates a corresponding metadata wrapper in the form of a DLL.

One RCW instance is created for each instance of the COM object.  The .NET runtime is concerned only with managing the lifetime of the RCW and garbage collects the RCW. It's the RCW that takes care of maintaining reference counts on the COM object that it's mapped to, thereby shielding the .NET runtime from managing the reference counts on the actual COM object. Any time a COM method raises an error, the COM error is trapped by the RCW. This error is then converted into an equivalent COMException class. The .NET client can catch the error with the usual try-catch exception handling mechanism, and the client has access to the error number, description, the source of the exception etc.

·        We can create an assembly by Assembly Generation tool called al.exe. This tool takes one or more .Net files or module and generates a single assembly.

 

·        By default, all the .Net assemblies are private. To share an assembly, you must create a shared name (also known as a strong name) for the assembly by using the Strong Name tool (sn.exe) that accompanies the .NET SDK. Using this tool first we generates a key file. That key file contains a globally unique identifier. Then we specify this key file into one of modules that the assembly is going to contains.

 

·        Using Assembly Cache Viewer (shfusion.dll), we can view detailed information on a shared assembly.

 

·        Global Assembly Cache tool enables you to perform various tasks on the assemblies:

 

Installing an assembly into GAC:

 

gacutil -i HelloWorld.DLL

 

Uninstalling as assembly from GAC:

 

gacutil -u HelloWorld, ver=1,0,0,0

 

(If you don't specify the version, all the assemblies with the same name would be uninstalled.)

 

 

 

·        Assembly's version number comes in the format: (<major><minor><build><revision>). Versioning pertains only to share assemblies—private assemblies need not apply.

 

Quick Fix Engineering updates, or hot fixes typically doesn't modify the code's interface, the chances that client code will be adversely affected are minimal. Therefore, the default versioning policy is to automatically associate all client code to the new "fixed" version of the code unless a configuration file exists for the application that explicitly associates the application with a specific version of an assembly. A new version of an assembly is considered to be a QFE if the only part of the version number that changed is the revision part.

Comments

Popular posts from this blog

Top Open Source Web-Based Project Management Software

This is an user contributed article. Project management software is not just for managing software based project. It can be used for variety of other tasks too. The web-based software must provide tools for planning, organizing and managing resources to achieve project goals and objectives. A web-based project management software can be accessed through an intranet or WAN / LAN using a web browser. You don't have to install any other software on the system. The software can be easy of use with access control features (multi-user). I use project management software for all of our projects (for e.g. building a new cluster farm) for issue / bug-tracking, calender, gantt charts, email notification and much more. Obviously I'm not the only user, the following open source software is used by some of the biggest research organizations and companies world wild. For example, NASA's Jet Propulsion Laboratory uses track software or open source project such as lighttpd / phpbb use re

My organization went through the approval process of supporting the .NET Framework 2.0 in production. Do we need to go through the same process all...

My organization went through the approval process of supporting the .NET Framework 2.0 in production. Do we need to go through the same process all over again for the .NET Framework 3.0? Do I need to do any application compatibility testing for my .NET Framework 2.0 applications? Because the .NET Framework 3.0 only adds new components to the .NET Framework 2.0 without changing any of the components released in the .NET Framework 2.0, the applications you've built on the .NET Framework 2.0 will not be affected. You don’t need to do any additional testing for your .NET Framework 2.0 applications when you install the .NET Framework 3.0.

Google products for your Nokia phone

Stay connected with Gmail, Search, Maps and other Google products. Check products are available for your Nokia phone Featured Free Products Search - Find the information you need quickly and easily Maps - Locate nearby businesses and get driving directions Gmail - Stay connected with Gmail on the go YouTube - Watch videos from anywhere Sync - Synchronize your contacts with Google