Pages

Men

rh

6/16/2012

New Features of C# 2.0


Features of C# 2.0
  •  Generics
  •  Iterator pattern(the Yield Keyword)
  •  Anonymous Method, 
  •  Nullable Types,
  • Null coalescing operator(??)
  • Partial classes
  • Static Classes
Generics :
Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.
  
Generics Overview
  • Use generic types to maximize code reuse, type safety, and performance.
  • The most common use of generics is to create collection classes.
  • The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. These should be used whenever possible in place of classes such as ArrayList in the System.Collections namespace.
  • You can create your own generic interfaces, classes, methods, events and delegates.
  • Generic classes may be constrained to enable access to methods on particular data types.
  • Information on the types used in a generic data type may be obtained at run-time by means of reflection.
 
Generic Type Parameter:

In a generic type or method definition, a type parameters is a placeholder for a specific type that a client specifies when they instantiate a variable of the generic type.

Example:
        
         GenericList<float> list1 = new GenericList<float>();
        GenericList<ExampleClass> list2 = new GenericList<ExampleClass>();
        GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();
 
Generic Collections:
 
A generic collection is strongly typed (type safe), meaning that you can only put one type of object into it. This eliminates type mismatches at runtime. Another benefit of type safety is that performance is better with value type objects because they don't incur overhead of being converted to and from type object

Generic Classes:
Generic classes encapsulate operations that are not specific to a particular data type.  The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees and so on where operations such as adding and removing items from the collection are performed in much the same way regardless of the type of data being stored.

When creating your own generic classes, important considerations include the following:
  • Which types to generalize into type parameters?
  • As a rule, the more types you can parameterize, the more flexible and reusable your code becomes. However, too much generalization can create code that is difficult for other developers to read or understand.
  • What constraints, if any, to apply to the type parameters.
  • A good rule is to apply the maximum constraints possible that will still let you handle the types you must handle. For example, if you know that your generic class is intended for use only with reference types, apply the class constraint. That will prevent unintended use of your class with value types, and will enable you to use the as operator on T, and check for null values.
  • Whether to factor generic behavior into base classes and subclasses.
  • Because generic classes can serve as base classes, the same design considerations apply here as with non-generic classes. See the rules about inheriting from generic base classes later in this topic.
  • Whether to implement one or more generic interfaces.
Example:
               class BaseNode { }
               class BaseNodeGeneric<T> { }

               // concrete type
               class NodeConcrete<T> : BaseNode { }

               //closed constructed type
               class NodeClosed<T> : BaseNodeGeneric<int> { }
               //open constructed type         
               class NodeOpen<T> : BaseNodeGeneric<T> { }

Generic classes that inherit from open constructed types must supply type arguments for any base class type parameters that are not shared by the inheriting class, as demonstrated in the following code:

class BaseNodeMultiple<T, U> { }

//No error
class Node4<T> : BaseNodeMultiple<T, int> { }
//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {}


Generic classes that inherit from open constructed types must specify constraints that are a superset of, or imply, the constraints on the base type:

Class NodeItem<T>where T: System.IComparable<T>, new () { }
Class SpecialNodeItem<T>: NodeItem<T>where T: System.IComparable<T>, new() { }


Generic types can use multiple type parameters and constraints, as follows:

Class SuperKeyType<K, V, U>
Where U: System.IComparable<U>
Where V: new()
{
}

Generic Methods 

You can create generic methods that can operate on any possible data type.To define a generic method you 
specify the type parameter after the method name and before the parameters list.
 

public void MyGenericMethod<T>(T x, T y)
{
    Console.Writeline("Parameters type is {0}", typeof(T));
}
 
You can define the type you want at the time you invoke the method.
 
int x, y;
MyGenericMethod<int>(x, y);
 
The result will be  Parameter type is System.Int32

string x, y;
MyGenericMethod<string>(x, y);
 
The result will be Parameter type is System.String

You can also create a generic method with out parameters as follow:
 
public void MyGenericMethod<T>()
{
    T x;
    Console.WriteLine("The type of x is  {0}", typeof(T));
}

 
Generic interface:

Generic Interface often useful to define interfaces either for generic collection classes, or for the generic classes that represent items in the collection. With generic classes it is preferable to use generic interfaces, such as IComparable<T> rather than IComparable, in order to avoid boxing and unboxing operations on value types.

When an interface is specified as a constraint on a type parameter, only types that implement the interface can be used.

Example:
  
  public interface ICounter<T>
 { 

 }
                       
You should also add the members that the implementers will have to override. Here is an example:

public interface ICounter<T>
{
   int Count { get; }
   T Get(int index);
}


In the same way, you can derive a generic interface from another generic interface. Here is an example:

public interface ICounter<T>
 { 

 int Count { get; } 

 T Get(int index); 

 } 
 public interface IPersons<T> : ICounter<T>
 { 
 void Add(T item); 
 }

Implementing a Generic Interface

After creating the generic interface, when deriving a class from it, follow the formula we reviewed for inheriting from a generic class. Here is an example:
public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IPersons<T> : ICounter<T>
{
    void Add(T item);
}

public class People<T> : IPersons<T>
{
   
}
When implementing the derived class, you must observe all rules that apply to interface derivation. That is, you must implement all the members of the generic interface. Of course, you can also add new members if you want. Here is an example:
public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IPersons<T> : ICounter<T>
{
    void Add(T item);
}

public class People<T> : IPersons<T>
{
    private int size;
    private T[] persons;

    public People()
    {
        size = 0;
        persons = new T[10];
    }

    public int Count { get { return size; } }

    public void Add(T pers)
    {
        persons[size] = pers;
        size++;
    }

    public T Get(int index) { return persons[index]; }
}
After implementing the interface, you can declare a variable of the class and use it as you see fit. Here is an example:
using System;

public interface ICounter<T>
{
    int Count { get; }
    T Get(int index);
}

public interface IPersons<T> : ICounter<T>
{
    void Add(T item);
}

public class People<T> : IPersons<T>
{
    private int size;
    private T[] persons;

    public People()
    {
        size = 0;
        persons = new T[10];
    }

    public int Count { get { return size; } }

    public void Add(T pers)
    {
        persons[size] = pers;
        size++;
    }

    public T Get(int index) { return persons[index]; }
}

public class Employee
{
    public long EmployeeNumber { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public double HourlySalary { get; set; }

    public Employee(long number = 0, string fName = "John",
                    string lName = "Doe", double salary = 12.05D)
    {
        EmployeeNumber = number;
        FirstName = fName;
        LastName = lName;
        HourlySalary = salary;
    }

    public override string ToString()
    {
        base.ToString();

        return string.Format("================================\n" +
                             "Employee Record\n" +
                             "--------------------------------\n" +
                             "Employee #:    {0}\nFirst Name:    {1}\n" +
                             "Last Name:     {2}\nHourly Salary: {3}",
                             EmployeeNumber, FirstName,
                             LastName, HourlySalary);
    }
}

public class Exercise
{
    public static int Main()
    {
        IPersons<Employee> employees = new People<Employee>();

        Employee empl = null;

        empl = new Employee();
        empl.EmployeeNumber = 253055;
        empl.FirstName = "Joseph";
        empl.LastName = "Denison";
        empl.HourlySalary = 12.85;
        employees.Add(empl);

        empl = new Employee();
        empl.EmployeeNumber = 204085;
        empl.FirstName = "Raymond";
        empl.LastName = "Ramirez";
        empl.HourlySalary = 9.95;
        employees.Add(empl);

        empl = new Employee();
        empl.EmployeeNumber = 970044;
        empl.FirstName = "Christian";
        empl.LastName = "Riley";
        empl.HourlySalary = 14.25;
        employees.Add(empl);

        for (int i = 0; i < employees.Count; i++)
        {
            Employee staff = employees.Get(i);

            Console.WriteLine("--------------------------------");
            Console.WriteLine("Employee #:    {0}", staff.EmployeeNumber);
            Console.WriteLine("First Name:    {0}", staff.FirstName);
            Console.WriteLine("Last Name:     {0}", staff.LastName);
            Console.WriteLine("Hourly Salary: {0}", staff.HourlySalary);
        }

        return 0;
    }
}
This would produce:
--------------------------------
Employee #:    253055
First Name:    Joseph
Last Name:     Denison
Hourly Salary: 12.85
--------------------------------
Employee #:    204085
First Name:    Raymond
Last Name:     Ramirez
Hourly Salary: 9.95
--------------------------------
Employee #:    970044
First Name:    Christian
Last Name:     Riley
Hourly Salary: 14.25
Press any key to continue . . .

Generic Delegates (C# Programming Guide)

A delegate can define its own type parameters. Code that references the generic delegate can specify the type argument to create a closed constructed type, just like when instantiating a generic class or calling a generic method, as shown in the following example:

Public delegate void Del<T>(T item);

publicstaticvoid Notify(int i) { }

Del<int> m1 = new Del<int>(Notify);
Del<int> m2 = Notify;

Delegates defined within a generic class can use the generic class type parameters in the same way that class methods do.

class Stack<T>
{
T[] items;
int index;

publicdelegatevoid StackDelegate(T[] items);
}

Code that references the delegate must specify the type argument of the containing class, as follows:

Private static void DoWork (float[] items) { }

Public static void TestStack ()
{
Stack<float> s = new Stack<float>();
Stack<float>.StackDelegate d = DoWork;
}
Generic delegates are especially useful in defining events based on the typical design pattern because the sender argument can be strongly typed and no longer has to be cast to and from Object.  

Collections: 

      It represents set of objects that you can access stepping through each element in turn. In particular the set of objects can access using FOR EACH LOOP. 

  •  Object class is the base class of every type in .NET.
  •  All the collections implement IEnumerable interface that is extended by ICollection interface.
  •  IDictionary and IList are also interfaces for collection which are derived from ICollection as shown in the diagram.
What’s the difference between Lists & Dictionaries

Both lists and dictionaries are used to store collections of data. Assume we had the following declarations…
 
var dic = new Dictionary<string, long>();
var lst = new List<long>();
long data;

With a list, you simply add the item to the list and it will add the item to the end of the list.
lst.Add(data);

With a dictionary, you need to specify some sort of key and the data you want to add so that it can be uniquely identified.
dic.Add(uniquekey, data);

Because with a dictionary you now have unique identifier, in the background they provide all sort’s of optimized algorithms to find your associated data. What this means is that if you are wanting to access your data it is a lot faster than a List.

So when is it appropriate to use either class?

if we can guarantee that each item in my collection will have a unique identifier, then I will use Dictionaries instead of Lists as there is a considerable performance benefit when accessing each data item. If I cannot make this sort of guarantee, then by default I will use a list.
Dictionary will be sorted  using key where List will be sorted by the Object.

Iterator pattern(the Yield Keyword)

The "yield" keyword.  Basically it is used to iterate through objects returned by a method.
Here is a simple example that demonstrates how yield return can be used to maintain state in a method. Every time you call GetInt() you will receive a new incremented integer.

public static IEnumerable<int> GetInt()
{
   for (int i = 0; i < 5; i++)
       yield return i;
}

Here's the implementation.

class Program
{
        static void Main(string[] args)
        {
            foreach (int i in GetInt())
                Console.WriteLine("Got " + i.ToString());
        }
        public static IEnumerable<int> GetInt()
        {
            for (int i = 0; i < 5; i++)
                yield return i;
        }
}

Usually, the "yield" keyword will be useful when you implement the GetEnumerator() method of the IEnumerable interface

class TestClass: IEnumerable<int>
{
        #region IEnumerable<int> Members
        public IEnumerator<int> GetEnumerator()
        {
            for (int i = 0; i < 5; i++)
                yield return i;
        }
        #endregion
        #region IEnumerable Members
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
     }

Anonymous methods
This is a new language feature in C# 2.0. This is a way to pass a code block as a delegate parameter
 
               button1.Click += delegate(System.Object o, System.EventArgs e)
                   { System.Windows.Forms.MessageBox.Show("Click!"); };

By using anonymous methods, you reduce the coding overhead in instantiating delegates by eliminating 
the need to create a separate method.


For example, specifying a code block in the place of a delegate can be useful in a situation when having to create a method might seem an unnecessary overhead. A good example would be when launching a new thread. This class creates a thread and also contains the code that the thread executes, without the need for creating an additional method for the delegate.
 
 
Void StartThread ()
{
    System.Threading.Thread t1 = new System.Threading.Thread
      (Delegate ()
            {
                System.Console.Write ("Hello, ");
                System.Console.WriteLine ("World!");
            });
    t1.Start ();
}

Example:
The following example demonstrates the two ways of instantiating a delegate:
  • Associating the delegate with an anonymous method.
  • Associating the delegate with a named method (DoWork).
In each case, a message is displayed when the delegate is invoked.

// declare a delegate
Delegate void Printer (string s);
 
Class TestClass
{
    Static void Main ()
    {
        // Instatiate the delegate type using an anonymous method:
        Printer p = delegate (string j)
        {
            System.Console.WriteLine (j);
        };
 
        // Results from the anonymous delegate call:
        p("The delegate using the anonymous method is called.");
 
        // the delegate instantiation using a named method "DoWork":
        p = new Printer (TestClass.DoWork);
 
        // Results from the old style delegate call:
        p ("The delegate using the named method is called.");
    }
 
    // the method associated with the named delegate:
    Static void DoWork (string k)
    {
        System.Console.WriteLine (k);
    }
}



Output:
The delegate using the anonymous method is called.
The delegate using the named method is called.

Nullable Types:

  •  Nullable types are instances of the System. Nullable struct.
  •  A Nullable type can represent the normal range of values for its underlying value type, plus an additional null value.
  • Nullable types represent value-type variables that can be assigned the value of null. You cannot create a Nullable type based on a reference type. (Reference types already support the null value.)
  • The syntax T? is shorthand for System. Nullable<T>, where T is a value type. The two forms are interchangeable.
  • Assign a value to a Nullable type in the same way as for an ordinary value type, for example int? x = 10; or double? d = 4.108;
  • Use the HasValue and Value read-only properties to test for null and retrieve the value, for example if(x.HasValue) j = x.Value;
  • The HasValue property returns true if the variable contains a value, or false if it is null.
  • The Value property returns a value if one is assigned, otherwise a  System.InvalidOperationException is thrown.
  • The default value for a Nullable type variable sets HasValue to false. The Value is undefined.
  • Use the?? Operator to assign a default value that will be applied when a Nullable type whose current value is null is assigned to a non-Nullable type, for example int? x = null; int y = x ?? -1;
  • Nested Nullable types are not allowed. The following line will not compile: Nullable<Nullable<int>> n;
  • For example, a Nullable<Int32>, pronounced "Nullable of Int32," can be assigned any value from -2147483648 to 2147483647, or it can be assigned the null value. A Nullable<bool> can be assigned the values true or false, or null.
Example:

Class NullableExample
{
    Static void Main ()
    {
        int? num = null;
        If (num.HasValue == true)
        {
            System.Console.WriteLine ("num = " + num.Value);
        }
        else
        {
            System.Console.WriteLine ("num = Null");
        }
        //y is set to zero
        Int y = num.GetValueOrDefault ();
// num.Value throws an InvalidOperationException if num.HasValue is false
        Try
        {
            y = num.Value;
        }
        Catch (System.InvalidOperationException e)
        {
            System.Console.WriteLine (e.Message);
        }
    }
}
 
The above will display the output:
num = Null
Nullable object must have a value.
Null coalescing operator (??):
The ?? Operator is called the null-coalescing operator and is used to define a default value for a nullable value types as well as reference types. It returns the left-hand operand if it is not null; otherwise it returns the right operand.

Example:
int b = a ?? -1;   
it means if the value of a is null then b=a otherwise the value of b will be -1.

Simply this means
If (a == null)    
     b = a; 
Else 
   b = -1; 
//The?? Operator also works with reference types:
    //message = param, unless param is null
    //in which case message = "No message"
    String message = param?? "No message";

To use the null-coalescing-operator, there are some compile-time ground rules.
  • The left-hand-side must evaluate to a reference or nullable type.
  • All evaluated statements must be of matching type, unless they can be implicitly converted.
Partial classes

A partial class is a class whose definition is present in 2 or more files. Each source file contains a section of the class, and all parts are combined when the application is compiled.
To split a class definition, use the partial keyword as shown in the example below. Student class is split into 2 parts. The first part defines the study() method and the second part defines the Play() method. When we compile this program both the parts will be combined and compiled. Note that both the parts uses partial keyword and public access modifier.

EXAMPLE


using System;
namespace PartialClass
{
public partial class Student
{
public void Study()
{
Console.WriteLine("I am studying");
}
}
public partial class Student
{
public void Play()
{
Console.WriteLine("I am Playing");
}
}
public class Demo
{
public static void Main()
{
Student StudentObject = new Student();
StudentObject.Study();
StudentObject. Play ();
}
}
}
The following points in mind when creating partial classes.   
  •  All the parts must use the partial keyword.
  •  All the parts must be available at compile time to form the final class.
  •  All the parts must have the same access modifiers - public, private, protected etc.
  • Any class members declared in a partial definition are available to all the other parts.
  • The final class is the combination of all the parts at compile time.
Advantages of using Partial Classes
  •  When working on large projects, spreading a class over separate files enables multiple programmers to work on it at the same time.
  •  When working with automatically generated source, code can be added to the class without having to recreate the source file. Visual Studio uses this approach when it creates Windows Forms, Web service wrapper code, and so on. You can create code that uses these classes without having to modify the file created by Visual Studio.
Static Classes:

  • A class can be declared static, indicating that it contains only static members.
  • It is not possible to create instances of a static class using the new keyword.
  • Static classes are loaded automatically by the .NET Framework common language runtime (CLR)
     when the program or namespace containing the class is loaded.
The main features of a static class are:
  • They only contain static members.
  • They cannot be instantiated.
  • They are sealed. 
  • They cannot contain Instance Constructors
  •  Creating a static class is therefore much the same as creating a class that contains only static members and a private constructor. A private constructor prevents the class from being instantiated.
  • The advantage of using a static class is that the compiler can check to make sure that no instance members are accidentally added. The compiler will guarantee that instances of this class cannot be created.
  • Static classes are sealed and therefore cannot be inherited. Static classes cannot contain a constructor, although it is still possible to declare a static constructor to assign initial values or set up some static state.
Static Methods:
When a method declaration includes a static modifier, that method is said to be a static method.
A static method does not operate on a specific instance, and it is a compile-time error to refer to this in a static method.

When a method is referenced in a member-access of the form E.M, if M is a static method, E must denote a type containing M, If M is an instance method E must denote an instance of a type containing M.

Instance Methods:

When no static modifier is present, the method is said to be an instance method.
An instance method operates on a given instance of a class, and that instance can be accessed as this




 

No comments :

Post a Comment