Archive for October 2013

Generics in .Net

October 24, 2013

Introduction

Generics allow you to define type-safe data structures, without committing to actual data types. This results in a significant performance boost and higher  quality code, because you get to reuse data processing algorithms without duplicating type-specific code.

EX.

The following example shows the full implementation of the Object-based stack, where it provides the classic Push() and Pop() methods. When developing a general-purpose stack, you would like to use it to store instance of various types:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Generic1

{

public class Stack

{

readonly int m_Size;

int m_StackPointer = 0;

object[] m_Items;

public Stack(): this(100)

{ }

public Stack(int size)

{

m_Size = size;

m_Items = new object[m_Size];

Console.WriteLine(“m_Size= {0}”, m_Size);

}

public void Push(object item)

{

if (m_StackPointer > m_Size)

throw new StackOverflowException();

m_Items[m_StackPointer] = item;

m_StackPointer++;

}

public object Pop()

{

             m_StackPointer–;

if (m_StackPointer >= 0)

{

return m_Items[m_StackPointer];

}

else

{

m_StackPointer = 0;

throw new InvalidOperationException(“Cannot pop an empty stack”);

}

         }

}

class Program

{

static void Main(string[] args)

{

Stack stack = new Stack();

stack.Push(“1”);

stack.Push(“2”);

stack.Push(“3”);

stack.Push(“4”);

stack.Push(“5”);

string number;

//string number = (string)stack.Pop();

//Console.WriteLine(“This is the number in the stack: {0}”, number);

// Console.ReadKey();

for (int i=0; i<=4 ;i++)

{

number = (string)stack.Pop();

Console.WriteLine(“Value in the Stack = {0}”,number);

}

Console.ReadKey();

stack.Push(1);

stack.Push(2);

int number1;

for (int i = 0; i <= 1; i++)

{

number1 = (int)stack.Pop();

Console.WriteLine(“Value in the Stack = {0}”, number1);

}

Console.ReadKey();

}

}

}

NOTE

Under C# 1.1, you have to use an Object-based stack meaning that the internal data type used in the stack is an object, and the stack methods interact with objects. The above example shows the full implementation of the Object-based stack. Because Object-based stack was used, the stack can hold any type of items:

Problem with Object-based solutions

  • When using value types, you have to box them in order to push and stor them, and unbox the value types when popping them off the stack. Boxing and unboxing incurs a significant performance penalty in their own right, and aslo increases the pressure on the managed heap resulting in more garbage collections, which is not great for performance either.
  •  Even when using reference types instead of value types, there is still a performance penalty because you have to cast from an Object to the actual type you interact with and incure the casting cost.
  •  Because the compiler lets you cast anything to and from object, you lose compile-time type safety. For example, the following code compiles fine,  but raises an invlaid cast exception at run time:

Stack stack = new Stack();

stack.Push(1);

//This compiles, but is not type safe, and will throw an exception:

string number = (string)stack.Pop();

You can overcome these two problems by providing a type-specific (and hence, type-safe) performant stack. For integers you can implement and use the IntStack

public class IntStack

{

int[] m_Items;

public void Push(int item) {}

public int Pop() {}

}

IntStack stack = new IntStack();

stack.Push(1);

int number = stack.Pop();

For strings you would implement the StringStack:

public class StringStack

{

string[] m_Items;

public void Push(string item) {}

public string Pop() {}

}

StringStack stack = new StringStack();

stack.Push(“1”);

string number = stack.Pop();

Unfortunately, solving the performance and type-safety problems this way introduces a third problem – productivity impact. Writing type-specific data structures is a tedious, repetitive, and error-prone (inclination) task. When fixing a defect in the data strcuture, you have to fix it not just in one place, but in as many places as there are type-specific duplicates of what is essentially the same data structure. In addition, there is no way to  foresee the use of unkown or yet-undefined future types, so you have to keep an Object-based data structure as well.

Generics allow you to define type-safe classes without compromising type safety performacne, or productivity. With generics, you implement the server only once as a generic server, while at the same time you can  declare and use it with any type. To do that, use the < and> backets, enclosing a generic type parameter.

EX.

public class Stack<T>

{

T[] m_Items;

public void Push(T item)

{}

public T Pop()

{}

}

Stack<int> stack = new Stack<int>();

stack.Push(1);

stack.Push(2);

int number = stack.Pop();

T is the generic type parameter (or type parameter) while the generic type is the Stack<T>. The int in Stack<int> is the type argument.  The advantage of this programming model is that the internal algorithm and data manipulation remian the same while the actual data type can change based on the way the client uses your server code:

EX.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Generic2

{

public class Stack<T>

{

readonly int m_Size;

int m_StackPointer = 0;

T[] m_Items;

public Stack(): this(100)

{ }

public Stack(int size)

{

m_Size = size;

m_Items = new T[m_Size];

}

public void Push(T item)

{

if (m_StackPointer >= m_Size)

throw new StackOverflowException();

m_Items[m_StackPointer] = item;

m_StackPointer++;

}

public T Pop()

{

m_StackPointer–;

if (m_StackPointer >= 0)

{

return m_Items[m_StackPointer];

}

else

{

m_StackPointer = 0;

throw new InvalidOperationException(“Cannot pop an empty stack”);

}

}

}

class Program

{

static void Main(string[] args)

{

            Stack<string> stack = new Stack<string>();

stack.Push(“1”);

stack.Push(“2”);

stack.Push(“3”);

stack.Push(“4”);

stack.Push(“5”);

string number;

//string number = (string)stack.Pop();

//Console.WriteLine(“This is the number in the stack: {0}”, number);

// Console.ReadKey();

for (int i = 0; i <= 4; i++)

{

number = (string)stack.Pop();

Console.WriteLine(“Value in the Stack = {0}”, number);

}

Console.ReadKey();

Stack<int> stack2 = new Stack<int>();

stack2.Push(1);

stack2.Push(2);

int number1;

for (int i = 0; i <= 1; i++)

{

number1 = (int)stack2.Pop();

Console.WriteLine(“Value in the Stack = {0}”, number1);

}

Console.ReadKey();

}

}

}

In .Net 2.0 generic have natice support in IL (intermediate language) and the CLR itsefl. When cyou compile generic C# server-side code, the compiler compiles it into IL, just like any other type. However, the IL only contains parameters or place holders for the actual specific types. In addition, the metadata of the generic server contains generic information.

In client-side compiler uses that generic metadata to support type safety. When the client prodives a specific type instead of a generic type parameter, the client’s compiler substitues the generic type parameter in the server metadata with the specified type argument. This provides the client’s compiler with type-specific definition of the server, as if generics were never involved. This way the client compiler can enforce correct methods parameters, type-safety checks, and even type-specific IntelliSence.

How does .Net compile the generic IL of the server to machine code

The actual machine code produced depends on whether the specified types are value or reference type. If the client specifies a value type, then the JIT compiler replaces the generic type parameters  in the IL with the specific value type, and compiles it to native code. However, the JIT compiler is asked to compile the generic server with a value type it have already compiled to machine code, it simply returns a reference to that server code.  Because the JIT compiler uses the same value-type-specific server code in all further encounters, there is no code bloating.

If the client specifies a reference type, then the JIT compiler replaces the generic parameters in the server IL with Object, and compiles it into native code. That code will be used in any further request for a reference type instead of a generic type parameter. The JIT compiler only reuses actual code, instances are still allocated according to their size off the managed heap, and there is no casting.

Generic Benefits

Generic in .Net let you reuse code and the effort you put into implementing it. The types and internal data can change without causing code bloat, regardless of whether you are using value or reference types.

You can develop, test, and deploy your code once, reuse it with any type, including future types, all with fill compiler support and type safety. Because the generic code does not force the boxing and unboxing of value types, or the down casting of reference types, performance is greatly improved.

With value types there is typically a 200% performance gain, and with reference types, you can expect up to a 100% performance gain in accessing the type (The application as a whole may or may not experience any performance improvements).

You can use generics in classes and in structs:

EX.

public struct Point<T>

{

public T X;

public T Y;

}

  • You can use the generic point for integer coordinates:

Point<int> point;

point.X = 1;

point.Y = 2;

  • Or for charting coordinates that require floating point precision:

Point<double> point;

point.X = 1.2;

point.Y = 3.4;

NOTE

Suppose instead of throwing an exception when the stack is empty, you would like to return the default value of the type stored in the stack. If you were using an Object-based stack,  you would simply return null, but a generic stack could be used with value types as well. To address this  issue, you can use the default() operator, which returns the default value of a type.

EX.

You can use default in the implementation of the Pop() method:

public T Pop()

{

m_StackPointer–;

if (m_StackPointer >=0)

{

return m_Items[m_StackPointer];

}

else

{

m_StackPointer = 0;

return default(T);

}

}

Multiple Generic Types

A single type can define multiple generic-type parameters:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Generic3

{

class Node<K, T>

{

public K Key;

public T Item;

public Node<K, T> NextNode;

public Node()

{

Key = default(K);

Item = default(T);

NextNode = null;

}

         public Node(K key, T item, Node<K, T> nextNode)

{

Key = key;

Item = item;

NextNode = nextNode;

}

     }

     public class LinkedList<K, T> where K:IComparable

{

Node<K, T> m_Head;

T Find(K key)

{

Node<K,T> current = m_Head;

while (current.NextNode !=null)

{

if (current.Key.CompareTo(key)==0 )

break;

else

current = current.NextNode;

}

return current.Item;

}

public T this[K key]

{

             get { return Find(key); }

         }

         public LinkedList()

{

m_Head = new Node<K, T>();

}

         public void AddHead(K key, T item)

{

Node<K, T> newNode = new Node<K, T>(key, item, m_Head.NextNode);

m_Head.NextNode = newNode;

}

}

class Program

{

static void Main(string[] args)

{

LinkedList<int, string> MyList = new LinkedList<int, string>();

MyList.AddHead(123, “AAA”);

MyList.AddHead(555, “BBB”);

string item = MyList[123];

Console.WriteLine(“Item of my MyList {0}”, item);

Console.ReadKey();

}

}

}

NOTE

In the above exmaple, the LinkedList stores nodes:

class Node<K,T>

{}

Each node contains a key (of the generic type parameter K) and a value (of the generic type parameter T).

Each node also has a reference to the next node in the list. The linked list itself is defined in terms of the generic

type parameters K and T:

public class LinkedList<K,T>

{}

This allows the list to expose generic methods like AddHead();

public void AddHead(K key, T item);

You need to provide type arguments when instantiating a node, and again, you can use the LinkdList’s own generic type parameters:

public void AddHead(K key, T item)

{

Node<K, T> newNode = new Node<K, T>(key, item, m_Head.NextNode);

m_Head.NextNode = newNode;

}

When the client is using the LinkedList, the client has to provide type arguments. The client can choose integers  as keys and strings as data items:

LinkedList<int, string> list = new LinkedList,int, string>();

list.AddHead(123,”AAA”);

But the client can choose any other combination, such as a time stamp for keys:

LinkedList<DateTime,string> list = new LinkedList<DateTime, string>();

list.AddHead(DateTime.Now,”AAA”);

Sometimes is it useful to alias a particular combination of specific types. You can do that through the using statement. Note that the scope of aliasing across the project files in the same way you would with using namespaces:

using List = LinkedList<int, string>;

class ListClient

{

static void Main(string[] args)

{

List list = new List();

list.AddHead(123,”AAA”);

}

}

Generic Constraints

With C# generic, the compiler compiles the generic code into IL independent of any type arguments that the clients will use. As a result, the generic code could try to use methods, properties, or members of the generic type parameters that are incompatible with the specific type arguments the client uses.

This is unacceptable because it amounts to lack of type safety. In C# you need to instruct the compiler which constraints the client-specified types  must obey in order for them to be used instead of the generic type parameters. There are three types of constraints:

  • A derivation constraint – indicates to the compiler that the generic type parameter derives from a base type such as interface or a particular base class.
  •  A default constructor constraint – indicates to the compiler that the generic type parameter exposes a default public constructor (a public constructor with no parameters).
  •  A reference/value type constraint – constrains the generic type parameter to be a reference or a value type.

NOTE

  • A generic type can employ multiple constraints, and you even get IntelliSense reflecting the constraints when using the generic type parameter, such as suggesting methods or members from the base type. The client-side compiler only allows the client developer to use type that comply with the constraints, thus enforcing type safety.
  •  The client-side compiler only allows the client developer to use types that comply wit the constraints, thus enforcing type safety. For example, suppose you would like to add searching  capability to the LinkedList. To implement the search, you need to scan the list, compare each node’s key with the key you’re looking for, and return the item of the node whose key matches. The problem is that the following implementation of Find() doesn’t compile:

EX.

T Find(K key)

{

Node<K,T> current = m_Head;

while(current.NextNode !=null)

{

if(current.Key ==key) // will not compile

break;

else

current = current.NextNode;

}

return current.Item;

}

The reason is that the compiler will refuse to compile this line:

if(current.Key ==key)

The line above will not compile because the compiler doesn’t know whether K (or the actual type supplied by the client) supports the == operator. You could try to overcome the “==” operator limitation by using the IComparable interface:

public interface IComparable

{

int CompareTo(object obj);

}

CompareTo() return 0 if the object you compare to is equal to the object implementing the interface, so the Find() method could use t as follows:

if(current.Key.CompareTo(key) ==0)

Unfortunately, this doesn’t compile either because the compiler has no way of knowing whether K (or the actual type supplied by the client) is derived from IComparable. You could explicity case to IComparable to force the compiler to compile the comparing line, except doing so is at the expense of type safety:

if ((IComparable)(current.Key)).CompareTo(key) == 0)

If the type the client uses doesn’t derive from IComparable, it results in a run-time exception. In addition, when the Key type used is a value type instead of the key type parameter, you force a boxing of the key, and that may have some performance implications.

Derivation Constraints

In C# 2.0 you use the “where” reserved keyword to define a constraint. Use the “where” keyword on the generic type parameter followed by a derivation colon to indicate to the compiler that the generic type parameter implements a particular interface.

EX.

public class LinkedList<K, T> where K:IComparable

{

Node<K, T> m_Head;

T Find(K key)

{

Node<K,T> current = m_Head;

while (current.NextNode !=null)

{

//if((IComparable)(current.Key)).CompareTo(key) == 0)

                 if (current.Key.CompareTo(key)==0 )

break;

else

current = current.NextNode;

}

return current.Item;

}

public T this[K key]

{

             get { return Find(key); }

        }

         public LinkedList()

{

m_Head = new Node<K, T>();

}

         public void AddHead(K key, T item)

{

Node<K, T> newNode = new Node<K, T>(key, item, m_Head.NextNode);

m_Head.NextNode = newNode;

}

}

You will also get IntelliSense support on the methods of interfaces you contain. When the client declares a variable of type LinkedList providing a type argument for the list’s key, the client-side compiler will insist that the key type is derived from IComparable and will refuse to build the client code otherwise.

Note

Even tough the constraint allows you to use IComparable, it doesn’t eliminate the boxing penalty  when the key used is a value type, such as an integer. To overcome this, the System.Collection.Generic namespace defines the generic interface ICompabrable<T>:

public interface IComparable<T>

{

int CompareTo(T other);

bool Equals(T other);

}

You can constrain the key type parameter to support IComparable<T> with the Key’s type as the type parameter, and by doing so you gain not only type safety but also eliminate the boxing of value types when used as keys:

public class LinkdedList<K,T> where K : IComparable<K>

{}

All the types that supported IComparable in .Net 1.1 support IComparable<T> in .Net 2.0. This enables the use for keys of common types such as int, string, Guid, DateTime, and so on.

In C# 2.0, all constraints must appear after the actual derivation list of the generic class:

public class LinkedList<K,T>:IEnumerable<T> where K: IComparable<K>

{}

You can constrain multiple interfaces on the same generic type parameter, separated by a comma:

public class LinkedList<K,T> where K: IComparable<K>,IConvertible

You can provide constraints for every generic type parameter your class uses:

public class LinkedList<K,T> where K: IComparable<K>

Where T: ICloneabe

{}

You can have a base class constraint, meaning stipulating that the generic type parameter is derived from a particular base class:

public class MyBaseClass

{}

public class LinkedList<K,T> where K:MyBaseClass

{}

NOTE

  • At most one base class can be used in a constraint because C# doesn’t support multiple inheritance of implementation. Obviously, the base class you constrain cannot be a sealed class or a static class, and the compiler enforces that. In addition, You cannot constrain System.Delegate or System.Array as a base class.
  •  You can constrain both a base class and one or more interfaces, but the base class must appear first in the derivation constraint list:

public class LinkedList<K,T> where K:MyBaseClass, IComparable<K>

{}

  •  C# does allow you to specify another generic type parameter as a constriant:

public class MyClass<T,U> where T : U

{}

When dealing wit a derivation constraint, you can satisfy the constraint using the base type itself, not necessarily a strict sub class of it:

public interface iMyInterface

{}

public class MyClass<T> where T : IMyInterface

{}

MyClass<IMyInterface> obj = new MyClass<IMyInterface>();

Or

public class MyOtherClass

{}

public class MyClass<T> where T:MyOtherClass

{}

MyClass<MyOtherClass> obj =  new MyClass<MyOtherClass>();

When providing a derivation constraint, the base type (interface, base class) you constrain must have consistent visibility with that of the  generic type parameter you define. For instance, the following constraint is valid, because internal types can use public types:

public class MyBaseClass

{}

Internal class MySubClass<T> where T : MyBaseClass

{}

However, if the visibility of the two classes were reversed, such as:

internal calss MyBaseClass

{}

public class MySubClass<T> where T: MyBaseClass

{}

Then the compiler would issue an error, because no client from outside the assembly will ever be able to use the generic type MySubClass, rendering  MySubClass in effect as an internal rather than a public type. The reason outside clients cannot use MySubClass is that to declare a variable of type MySubClass, they require to make use of a type that derives from the internal type MyBaseClass.

Constructor Constraint

Suppose you want to instantiate a new generic object inside a generic class. The problem is the C# compiler does not know whether the type argument the client will use has a matching constructor, and it will refuse to compile the isntantiation line.

To address this problem, C# allows you to constrain a generic type parameter such that it must support a public default constructor. This is done using the new() constructor:

class Node<K,T> where T : new

{

public K Key;

public T Item;

public Node<K,T> NextNode;

public Node()

{

Key      = default(K);

Item = new T();

NextNode = null;

}

}

You can combine the constructor constraint with derivation constraint, provided the constructor constraint appears last in the constraint list:

public class LinkedList<K,T> where K:IComparable<K>,new()

{….}

Reference/Value Type Constraint

The C# compiler only lets you implicitly cast generic type parameters to Object, or to constraint-specified types. Such implicit casting is type safe because any incompatibility is discovered at compile-time.

EX.

interface ISomeInterface

{}

class BaseClass

{}

class MyClass<T> where T : BaseClass,ISomeInterface

{

void SomeMethod

{

ISomeInterface obj1 = t;

BaseClass          obj2 = t;

object                obj3 = t;

}

}

The compiler lets you explicitly cast generic type parameter to any other interface, but not to a class:

EX.

interface ISomeInterface

{}

class SomeClass

{}

class MyClass<T>

{

void SomeMethod(T t)

{

IomeInterface  obj1 = (ISomeInterface)t;  //Compiles

SomeClass        obj2 = (SomeClass)t;     //Does not compile

}

}

However, you can force a cast from a generic type parameter to any other type using a temporary Object variable:

EX.

class SomeClass

{}

class MyClass<T>

{

void SomeMethod(T t)

{

object temp = t;

SomeClass obj = (SomeClass)temp;

}

}

NOTE

Such explicit casting is dangerous because it may throw an exception at run time if the type argument  used instead of the generic type parameter does not derive from the type to which you explicitly case. Instead of risking a casting exception, a better approach is to use the “is” and “as” operators.

  • The “is” operator returns true if the generic type parameter is of the queried type.
  • The “as” will perform a cast if the types are compatible, and will return null otherwise.

You can use “is” and “as” o both generic type parameters and on generic classes with specific type arguments.

EX.

public class MyClass<T>

{

public void SomeMethod

{

if(t is int)

{..}

if(t is LinkedList<int, string>)

{}

string str = t as string;

if(str !=null)

{}

LinkedList<int, string> list = t as LinkedList<int, string>;

if(list != null)

{}

}

}

Inheritance and Generics

When deriving from a generic base class, you must provide a type argument instead of the base-class‘s generic type parameter:

EX.

public class BaseClass<T>

{}

public class SubClass : BaseClass<int>

{}

If the subclass is generic, you can use the subclass generic type parameter as the specified type for the generic base class:

EX.

public class SubClass<T> : BaseClass<T>

{….}

When using the subclass generic type parameters, you must repeat any constraints stipulated at the base class level at the subclass level:

EX.

Derivation Constraint

public class BaseClass<T>  where T : ISomeInterface

{}

public class SubClass<T> : BaseClass<T> where T : ISomeInterface

{}

Constructor Constraint

public class BaseClass<T>  where T : new()

{

public T SomeMethod()

{

return new T();

}

}

public class SubClass<T> : BaseClass<T> where T : new()

{}

A base class can define virtual methods whose signature use generic type parameters. When overriding them, the subclass must provide the corresponding type in the method signatures:

EX.

public class BaseClass<T>

{

public virtual T SomeMethod

{}

}

public class SubClass: BaseClass<int>

{

public override int SomeMethod

{}

}

If the subclass is generic it can also use its own generic type parameters for the override:

EX.

public class SubClass<T>: BaseClass<T>

{

public override T SomeMethod()

{….}

}

You can define generic interfaces, generic abstract classes, and even generic abstract methods. These types behave like any other generic base type:

EX.

public interface ISomeInterface<T>

{

T SomeMethod(T t);

}

Public abstract class BaseClass<T>

{

public abstract T SomeMethod(T t);

}

public class SubClass<T> : BaseClass<T>

{

public override T SomeMethod(T t)

{….}

}

In C# 2.0, it is impossible to use operators such as “+” or “+=” on generic type parameters. For exmaple, the following code does not compile because C# 2.0 doesn’t have operator constraints:

EX.

public class Calculator<T>

{

public T Add(T arg1, T arg2)

{

return arg1 + arg2;//Does not compile

}

//Rest of the methods

}

You can compensate using abstract methods (or interfaces) by defining generic operations. Since an abstract  method cannot have any code in it, you can specify the generic operations at the base class level, and provide  a concrete type and implementation at the subclass level:

EX.

public abstract class BaseCalculator<T>

{

public abstract T Add(T arg1, T arg2);

public abstract T Subtract(T arg1, T arg2);

public abstract T Divide(T arg1, T arg2);

public abstract T Multiply(T arg1, T arg2);

}

public class MyCalculator : BaseCalculator<int>

{

public override int Add(int arg1, int arg2)

{

return arg1 + arg2;

}

//Rest of the methods

}

A generic interface will yield a somewhat cleaner solution as well:

EX.

public interface ICalculator<T>

{

T Add(T arg1, T arg2);

//Rest of the methods

}

public class MyCalculator : ICalculator<int>

{

public int Add(int arg1, int arg2)

{

return arg1 + arg2;

}

//Rest of te methods

}

Generic Methods

In C# 2.0 a method can define generic type parameters, specific to its execution scope:

This is an important capability because it allows you to call the method with a different type every time, which is very handy for utility classes:

EX.

public class MyClass<T>

{

public void MyMethod<X>(X x)

{….}

}

You can define method-specific generic type parameters even if the containing class does not use generics at all:

EX.

public class MyClass

{

public void MyMethod<T>(T t)

{….}

}

When calling a method that defines generic type parameters, you can provide the type to use at the call site:

MyClass obj = new MyClass();

obj.MyMethod<int>(3);

EX.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace GenericMethod

{

class Program

{

public class MyClass

{

public void MyMethod<T>(T t)

{

Console.WriteLine(“This is a generic Method retunrs a value of {0}:”,t);

Console.ReadKey();

}

}

static void Main(string[] args)

{

MyClass obj = new MyClass();

obj.MyMethod<int>(3);

}

}

}

NOTE

  • The ability to define a method with specific generic type parameters is for methods only. Properties, or indexers can only use generic type parameters defined at the scope of the class.
  •  When the method is invoked the C# compiler is smart enough to infer the correct type based on the type of parameter passed in, and it allows omitting the type specification altogether:

EX.

public class MyClass

{

public void MyMethod<T>(T t)

{}

}

 MyClass obj = new MyClass();

obj.MyMethod(3);

EX.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace GenericMethod

{

class Program

{

public class MyClass

{

public void MyMethod<T>(T t)

{

Console.WriteLine(“This is a generic Method retunrs a value of {0}:”,t);

Console.ReadKey();

}

}

static void Main(string[] args)

{

MyClass obj = new MyClass();

obj.MyMethod(3);

}

}

}

NOTE

This ability is called generic type inference. Note that the compiler cannot infer the type based on the type of the returned value alone:

public class MyClass

{

public T MyMethod<T>()

{ }

}

MyClass obj = new MyClass();

int number = obj.MyMethod(); // Does not compile

When a method defines its own generic type parameters, it can also define constraints for these types:

public class MyClass

{

public  void SomeMethod<T>(T t) where T : IComparable<T>

{}

}

You cannot provide method-level constraints for class-level generic type parameters. All constraints for class-level generic type parameters must be defined at the class scope.

When overriding a virtual method that defines generic type parameters, the subclass method must redefine the method-specific generic type parameter:

EX.

public class BaseClass

{

public virtual void SomeMethod<T>(T t)

{….}

}

public class SubClass : BaseClass

{

public override void SomeMethod<T>(T t)

{}

}

The subclass implementation must repeat all constraints that appeared at the base method level:

public class BaseClass

{

public virtual void SomeMethod<T>(T t) where T:new()

{}

}

public class SubClass : BaseClass

{

public override void SomeMethod<T>(T t) where T:new()

{}

}

NOTE

  • The method override cannot define new constrains that did not appear at the base method.
  • If the subclass method calls the base class implementation of the virtual method, it must specify the type  argument to use instead of the generic base method type parameter. You can either explicitly specify it yourself  or rely on type inference if it is available:

EX.

public class BaseClass

{

public virtual void SomeMethod<T>(T t)

{….}

}

public class SubClass : BaseClass

{

public override void SomeMethod<T>

{

base.SomeMethod<T>(t);

base.SomeMethod(t);

}

}

Generic Static Method

C# allows you to define static methods for the containod that use generic type parameters. However, when invoking such a static method,  you need to provide the concrete type for the containing class at the call site, such as in this example:

EX.

public class MyClass<T>

{

public static T SomeMethod

{}

}

int number = MyClass<int>.SomeMethod(3);

Static methods can define method-specific generic type parameters and constraints, similar to instance methods. When calling such methods, you need to provide the method-specific types at the call site, either explictitly:

EX.

public class MyClass<T>

{

public static T SomeMethid<X>(T t, X x)

{}

}

int number = MyClass<int>.SomeMethod<string>(3,”AAA”);

Or rely on type interface when possible:

 int number = MyClass<int>.SomeMethod(3,”AAA”);

Generic static methods are subjected to all constraints imposed on the generic type paramter they use at the class level. As with instance method, you can provide constraints for generic type parameters defined by the static method:

EX.

public class MyClass

{

public static T SomeMethod<T>(T t) where T:IComparable<T>

{….}

}

Operators in C# are nothing more than static methods and C# allows you to overload operators for your generic types:

EX.

public class LinkedList<K,T>

{

public static LinkedList<K,T> operator+(LinkedList<K,T> lhs,LinkedList<K,T>rhs)

{

return concatenate(lhs,rhs);

}

static LinkedList<K,T> concatenate( LinkedList<K,T> list1, LinkedList<K,T> list2)

{

LinkedList<K,T> newList = new LinkedList<K,T>();

Node<K,T> current;

current = list2.m_Head;

while(current != null)

{

newList.AddHead(current.Key, current.Item);

current = current.NextNode;

}

current = list2.m_Head;

while(current !=null)

{

newList.AddHead(current.Key, current.Item);

current = current.NextNode;

}

return newList;

}

//Rest of LinkedList

}

EX.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace StaticMethod_Generics

{

public static class MathEx

{

public static T Max<T>(T first, params T[] values) where T : IComparable

{

T maximum = first;

foreach (T item in values)

{

if (item.CompareTo(maximum) > 0)

{

maximum = item;

}

}

return maximum;

}

public static T Min<T>(T first, params T[] values) where T : IComparable

{

T minimum = first;

foreach (T item in values)

{

if (item.CompareTo(minimum) < 0)

{

minimum = item;

}

}

return minimum;

}

}

class Program

{

static void Main(string[] args)

{

Console.WriteLine(MathEx.Max<int>(7, 490));

Console.WriteLine(MathEx.Min<string>(“R.O.U.S.”, “Fireswamp”));

Console.ReadKey();

}

}

}

EX.

using System;

using System.Collections.Generic;

class Program

{

static List<T> GetInitializedList<T>(T value, int count)

{

// This generic method returns a List with ten elements initialized.

// … It uses a type parameter.

// … It uses the “open type” T.

List<T> list = new List<T>();

for (int i = 0; i < count; i++)

{

list.Add(value);

}

return list;

}

static void Main()

{

// Use the generic method.

// … Specifying the type parameter is optional here.

// … Then print the results.

List<bool> list1 = GetInitializedList(true, 5);

List<string> list2 = GetInitializedList<string>(“Perls”, 3);

foreach (bool value in list1)

{

Console.WriteLine(value);

}

foreach (string value in list2)

{

Console.WriteLine(value);

}

Console.ReadKey();

}

}

Generic Delegates

A delegate defined in a class can take advantage of the generic type parameter of that class:

EX.

public class MyClass<T>

{

public delegate void GenericDelegate(T t);

public void SomeMethod(T t)

{….}

}

//When specifying a type for the containing class, it affects the delegate as well

MyClass<int> obj = new MyClass<int>();

MyClass<int>.GenericDelegate del;

del = new MyClass<int>.GenericDelegate(obj.SomeMethod);

del(3);

NOTE

C# 2.0 allows you to make a direct assignment of a method reference into a delegate variable:

This feature called “Delegate Inteference”. The compiler is capable of inferring the type of the delegate you assign into, finding if the target object has a method by the name you specify, and verifying that  the method’s singature matches. Then, the compiler creates a new delegate of the inferred argument type (including the correct type instead of the generic type parameter), and assigns the new delegate into the inferred delegate.

MyClass<int> obj = new MyClass<int>();

MyClass<int>.GenericDelegate del;

del = obj.SomeMethod;

Delegates defined outside the scope of a class can use generic type parameters. In that case, you have to provide the type arguments  for the delegate when declaring and instantiating it:

EX.

public delegate void GenericDelegate<T>(T t);

public class MyClass

{

pulic void SomeMethod(int number)

{….}

}

MyClass obj = new MyClass();

GenericDelegate<int> del;

del =  new GenericDelegate<int>(obj.SomeMethod);

del(3);

Alternatively, you can use delegate inference when assigning the delegate:

MyClass obk = new MyClass();

GenericDelegate<int> del;

del = obj.SomeMethod;

And naturally, a delegate can define constraints to accompany its generic type parameters:

public delegate void MyDelegate<T>(T t) Where T:IComparable<T>;

NOTE

The delegate-level constraints are enforced only on the using side, when delaring a delegate variable and instantiating  a delegate object, similar to any other constraint at the scope of types or methods.

Generics and Reflection

In .Net 2.0, refelection is extended to support generic type parameters. The type Type can ow represent generic types with specific type arguments (Called bounded types), or unspecified (unbounded) types. As in C# 1.1., you can obtain the Type of any type by using the typeof operator or by calling the GetType() method that every type supports.

EX.

public  class MyClass<T>

{

public  void SomeMethod(T t)

{

Type type = typeof(T);

Debug.Assert(type == t.GetType());

}

}

System.Array and Generics

The System.Array type is extended with many generic static methods. The generic static methods are designed to automate and streamline common  tasks of working with arrays, such as iterating over the array and performing an action on each element, scanning the array, or looking for a value that matches a certain (a predicate), converting and sorting the array, and so on.

EX.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

namespace Generics6

{

class Program

{

static void Main(string[] args)

{

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };

Action<int> trace = delegate(int number)

{

Trace.WriteLine(number);

};

Predicate<int> isPrime = delegate(int number)

{

switch(number)

{

case 1: case 2: case 3: case 5:case 7: case 11: case 13: case 17: case 19: return true;

default:

return false;

}

            };

Array.ForEach(numbers, trace);

int[] primes = Array.FindAll(numbers,isPrime);

Array.ForEach(primes, trace);

}

}

}

System.Array’s static generic methods all work with the following four generic delegates defined in the System namespace:

EX.

public delegate void Action<T>(T t);

public delegate int Comparison<T>(T x, T y);

public delegate U Converter<T, U>(T from);

public delegate bool Predicate<T>(T t);

int[] numbers = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};

Action<int> trace = delegate(int number)

{

Trace.WriteLine(number);

};

Predicate<int> isPrime = delegate(int number)

{

switch(number)

{

case 1: case 2 : case 3: case 5: case 7:

case 11: case 13: case 17: case 19:

return true;

default:

return false;

}

};

Array.ForEach(numbers, trace);

int[] primes = Array.FindAll(numbers, isPrime);

Array.ForEach(prime,tracte);

NOTE

Similar generic methods are also available on the class List<t> defined in the System.Collection.Generic namespace. These methods use the same four generic delegates.

Generic Collections

The data stracture in System.Collections are all Object-based, and thus inheriting the two problems: Infrerior performance, and  lack of type safety. .Net 2.0 introduces a set of generic collections in the System.Collections.Generic namespace. The following table maps the main types of System.Collections.Generic to those of System.Collections:

System.Collections.Generic

System.Collections

Comparer<T>

Comparer

Dictionary<K,T>

HashTable

LinkedList<T>

—-

List<T>

ArrayList

Queue<T>

Queue

SortedDictionary<K,T>

SortedList

Stack<T>

Stack

ICollection<T>

ICollection

IComparable<T>

System.IComparable

IDictionary<K,T>

IDictionary

IEnunerables<T>

IEnumerable

IEnumerable<T>

IEnumerator

IList<T>

IList

All the generic collections in System.Collections.Generic also impelment the generic IEnumerable<T> interface, defained as:

public interface IEnumerable<T>

{

IEnumerator<T> GetEnumerator();

}

public interface IEnumerator<T> : IDisposable

{

T Current{get;}

bool MoveNext();

}

IEnumerable<T> provides access to the IEnumerator<T> iterator interface, used for abstracted iterations over the collection.  All the collections implement IEnumerable<T> on a nested struct, where the generic type parameter T is the type the collection stores.

What Generics Cannot Do

  • Uner .Net 2.0, you cannot define generic Web Services (Web Methods that use generic type parameters). The reason is that none of the Web Service standards support generic services.
  • You also cannot use generic types on a serviced component. The reason is that generics do not meet COM visibility requirements, which are required for services components

Conclusion

C# generics are valuable part of your developement arsenal, they imrpove performance, type safety, and quality, reduce repetitive programming tasks simplify the overall programming model, and do so with elegant, readable syntace.

C# takes generics to a new level by providing compile-time safety and support. C# utilizes two-phase compilation, metadata, and innovative concepts such as constraints and generic methods.

 References

Lowy, J. (2005) An Introduction to C# Generics [Online]. Available from: http://msdn.microsoft.com/en-us/library/ms379564%28v=vs.80%29.aspx (Accessed: Oct. 24, 2013).

Zubinaraj (2007) Generics Explained (.Net Framework Version 2.0) [Online]. Available from: http://www.codeproject.com/Articles/8846/Generics-Explained-NET-Framework-version-2-0 (Accessed: Oct. 24, 2013).

Pandita, S (2009) .Net Generics in a Nutshell [Online]. Available from: http://www.codeproject.com/Articles/47850/NET-Generics-in-a-Nutshell (Accessed: Oct. 24, 2013).

Mayo, J. (2011) Intorduction to Generic Collections [Online]. Available from: http://csharp-station.com/Tutorial/CSharp/Lesson20 (Accessed: Oct. 24, 2013).

Surapureddy, S. (2008) Generics in .Net 2.0 Made Simple [Online]. Available from: http://www.c-sharpcorner.com/uploadfile/surapureddy/generics-in-net-2-0-made-simple/ (Accessed: Oct. 24, 2013).

Advertisements