An Introduction to C# Generics

An Introduction to C# Generics
Juval Lowy
August 2003
Summary: This article discusses the problem space generics address, how they are implemented, the
benefits of the programming model, and unique innovations, such as constrains, generic methods and
delegates, and generic inheritance. (41 pages)
Download the GenericsInCSharp.msi sample file.
Note This article assumes you are familiar with C# 1.1. For more information on the C# language, visit
http://msdn.microsoft.com/vcsharp/language .
Contents
Introduction
Generics Problem Statement
What Are Generics
Applying Generics
Generic Constraints
Generics and Casting
Inheritance and Generics
Generic Methods
Generic Delegates
Generics and Reflection
Generics and the .NET Framework
Conclusion
Introduction
Generics are the most powerful and anticipated feature of C# 2.0. 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. In concept, generics are similar to C++ templates, but are drastically different in
implementation and capabilities. This article discusses the problem space generics address, how they are
implemented, the benefits of the programming model, and unique innovations, such as constrains,
generic methods and delegates, and generic inheritance. You will also see how generics are utilized in
other areas of the .NET Framework such as reflection, collections, serialization, and remoting, and how
to improve on the basic offering.
Generics Problem Statement
Consider an everyday data structure such as a stack, providing the classic Push() and Pop() methods.
When developing a general-purpose stack, you would like to use it to store instances of various types.
Under C# 1.1, you have to use an Object-based stack, meaning that the internal data type used in the
stack is an amorphous Object, and the stack methods interact with Objects:
public class Stack
{
object[] m_Items;
public void Push(object item)
{...}
public object Pop()
{...}
}
Code block 1 shows the full implementation of the Object-based stack. Because Object is the
canonical .NET base type, you can use the Object-based stack to hold any type of items, such as integers:
Stack stack = new Stack();
stack.Push(1);
stack.Push(2);
int number = (int)stack.Pop();
Code block 1. An Object-based stack
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];
}
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");
}
}
}
However, there are two problems with Object-based solutions. The first issue is performance. When
using value types, you have to box them in order to push and store them, and unbox the value types
when popping them off the stack. Boxing and unboxing incurs a significant performance penalty in their
own right, but it also 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 incur the casting cost:
Stack stack = new Stack();
stack.Push("1");
string number = (string)stack.Pop();
The second (and often more severe) problem with the Object-based solution is type safety. 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 invalid 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();
And so on. Unfortunately, solving the performance and type-safety problems this way introduces a third,
and just as serious problem—productivity impact. Writing type-specific data structures is a tedious,
repetitive, and error-prone task. When fixing a defect in the data structure, 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 unknown or yet-undefined future types, so
you have to keep an Object-based data structure as well. As a result, most developers find type-specific
data structures to be impractical and opt for using Object-based data structures, in spite of their
deficiencies.
What Are Generics
Generics allow you to define type-safe classes without compromising type safety, performance, or
productivity. 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 > brackets, enclosing a generic type
parameter. For example, here is how you define and use a generic stack:
public class Stack

   1<t>   
   2{   
   3T[] m_Items;   
   4public void Push(T item)   
   5{...}   
   6public T Pop()   
   7{...}   
   8}   
   9Stack<int> stack = new Stack<int>();   
  10stack.Push(1);   
  11stack.Push(2);   
  12int number = stack.Pop();   
  13Code block 2 shows the full implementation of the generic stack. Compare Code block 1 to Code block   
  142 and see that it is as if every use of object in Code block 1 is replaced with T in Code block 2, except   
  15that the Stack is defined using the generic type parameter T:   
  16public class Stack<t>   
  17{...}   
  18When using a generic stack, you have to instruct the compiler which type to use instead of the generic   
  19type parameter T, both when declaring the variable and when instantiating it:   
  20Stack<int> stack = new Stack<int>();   
  21The compiler and the runtime do the rest. All the methods (or properties) that accept or return a T will   
  22instead use the specified type, an integer in the example above.   
  23Code block 2. The generic stack   
  24public class Stack<t>   
  25{   
  26readonly int m_Size;   
  27int m_StackPointer = 0;   
  28T[] m_Items;   
  29public Stack():this(100)   
  30{}   
  31public Stack(int size)   
  32{   
  33m_Size = size;   
  34m_Items = new T[m_Size];   
  35}   
  36public void Push(T item)   
  37{   
  38if(m_StackPointer &gt;= m_Size)   
  39throw new StackOverflowException();   
  40m_Items[m_StackPointer] = item;   
  41m_StackPointer++;   
  42}   
  43public T Pop()   
  44{   
  45m_StackPointer--;   
  46if(m_StackPointer &gt;= 0)   
  47{   
  48return m_Items[m_StackPointer];   
  49}   
  50else   
  51{   
  52m_StackPointer = 0;   
  53throw new InvalidOperationException("Cannot pop an empty   
  54stack");   
  55}   
  56}   
  57}   
  58Note T is the generic type parameter (or type parameter) while the generic type is the Stack<t>.   
  59The advantage of this programming model is that the internal algorithms and data manipulation remain   
  60the same while the actual data type can change based on the way the client uses your server code.   
  61Generics Implementation   
  62On the surface C# generics look syntactically similar to C++ templates, but there are important   
  63differences in the way they are implemented and supported by the compiler. As you will see later in this   
  64article, this has significant implications on the manner in which you use generics.   
  65Note In the context of this article, when referring to C++ it means classic C++, not Microsoft C++ with   
  66the managed extensions.   
  67Compared to C++ templates, C# generics can provide enhanced safety but are also somewhat limited in   
  68capabilities.   
  69In some C++ compilers, until you use a template class with a specific type, the compiler does not even   
  70compile the template code. When you do specify a type, the compiler inserts the code inline, replacing   
  71every occurrence of the generic type parameter with the specified type.. In addition, every time you use   
  72a specific type, the compiler inserts the type-specific code, regardless of whether you have already   
  73specified that type for the template class somewhere else in the application. It is up to the C++ linker to   
  74resolve this, and it is not always possible to do. This may results in code bloating, increasing both the load   
  75time and the memory footprint.   
  76In .NET 2.0, generics have native support in IL (intermediate language) and the CLR itself. When you   
  77compile generic C# server-side code, the compiler compiles it into IL, just like any other type. However,   
  78the IL only contains parameters or place holders for the actual specific types. In addition, the metadata   
  79of the generic server contains generic information.   
  80The client-side compiler uses that generic metadata to support type safety. When the client provides a   
  81specific type instead of a generic type parameter, the client's compiler substitutes the generic type   
  82parameter in the server metadata with the specified type. This provides the client's compiler with   
  83type-specific definition of the server, as if generics were never involved. This way the client compiler can   
  84enforce correct method parameters, type-safety checks, and even type-specific IntelliSense®.   
  85The interesting question is how does .NET compile the generic IL of the server to machine code. It turns   
  86out that the actual machine code produced depends on whether the specified types are value or reference   
  87type. If the client specifies a value type, then the JIT compiler replaces the generic type parameters in the   
  88IL with the specific value type, and compiles it to native code. However, the JIT compiler keeps track of   
  89type-specific server code it already generated. If the JIT compiler is asked to compile the generic server   
  90with a value type it has already compiled to machine code, it simply returns a reference to that server   
  91code. Because the JIT compiler uses the same value-type-specific server code in all further encounters,   
  92there is no code bloating.   
  93If the client specifies a reference type, then the JIT compiler replaces the generic parameters in the   
  94server IL with Object, and compiles it into native code. That code will be used in any further request for   
  95a reference type instead of a generic type parameter. Note that this way the JIT compiler only reuses   
  96actual code. Instances are still allocated according to their size off the managed heap, and there is no   
  97casting.   
  98Generics Benefits   
  99Generics in .NET let you reuse code and the effort you put into implementing it. The types and internal   
 100data can change without causing code bloat, regardless of whether you are using value or reference   
 101types. You can develop, test, and deploy your code once, reuse it with any type, including future types,   
 102all with full compiler support and type safety. Because the generic code does not force the boxing and   
 103unboxing of value types, or the down casting of reference types, performance is greatly improved. With   
 104value types there is typically a 200 percent performance gain, and with reference types you can expect   
 105up to a 100 percent performance gain in accessing the type (of course, the application as a whole may or   
 106may not experience any performance improvements). The source code available with this article includes   
 107a micro-benchmark application, which executes a stack in a tight loop. The application lets you   
 108experiment with value and reference types on an Object-based stack and a generic stack, as well as   
 109changing the number of loop iterations to see the effect generics have on performance.   
 110Applying Generics   
 111Because of the native support for generics in the IL and the CLR, most CLR-compliant language can take   
 112advantage of generic types. For example, here is some Visual Basic® .NET code that uses the generic   
 113stack of Code block 2:   
 114Dim stack As Stack(Of Integer)   
 115stack = new Stack(Of Integer)   
 116stack.Push(3)   
 117Dim number As Integer   
 118number = stack.Pop()   
 119You can use generics in classes and in structs. Here is a useful generic point struct:   
 120public struct Point<t>   
 121{   
 122public T X;   
 123public T Y;   
 124}   
 125You can use the generic point for integer coordinates, for example:   
 126Point<int> point;   
 127point.X = 1;   
 128point.Y = 2;   
 129Or for charting coordinates that require floating point precision:   
 130Point<double> point;   
 131point.X = 1.2;   
 132point.Y = 3.4;   
 133Besides the basic generics syntax presented so far, C# 2.0 overloads some of the syntax of C# 1.1 to   
 134specialize it when using generics. For example, consider the Pop() method of Code block 2. Suppose   
 135instead of throwing an exception when the stack is empty, you would like to return the default value of   
 136the type stored in the stack. If you were using an Object-based stack, you would simply return null, but   
 137a generic stack could be used with value types as well. To address this issue, every generic type   
 138parameter supports a property called default, which returns the default value of a type.   
 139Here is how you can use default in the implementation of the Pop() method:   
 140public T Pop()   
 141{   
 142m_StackPointer--;   
 143if(m_StackPointer &gt;= 0)   
 144{   
 145return m_Items[m_StackPointer];   
 146}   
 147else   
 148{   
 149m_StackPointer = 0;   
 150return T.default;   
 151}   
 152}   
 153The default value for reference types is null, and the default value for value types (such as integers,   
 154enum, and structures) is a zero whitewash (filling the structure with zeros). Consequently, if the stack is   
 155constructed with strings, the Pop() methods return null when the stack is empty, and when the stack is   
 156constructed with integers, the Pop() methods return zero when the stack is empty.   
 157Multiple Generic Types   
 158A single type can define multiple generic types parameters. For example, consider the generic linked list   
 159shown in Code block 3.   
 160Code block 3. Generic linked list   
 161class Node<k,t>   
 162{   
 163public Node()   
 164{   
 165Key = K.default;   
 166Item = T.default;   
 167NextNode = null;   
 168}   
 169public Node(K key,T item,Node<k,t> nextNode)   
 170{   
 171Key = key;   
 172Item = item;   
 173NextNode = nextNode;   
 174}   
 175public K Key;   
 176public T Item;   
 177public Node<k,t> NextNode;   
 178}   
 179public class LinkedList<k,t>   
 180{   
 181public LinkedList()   
 182{   
 183m_Head = new Node<k,t>();   
 184}   
 185public void AddHead(K key,T item)   
 186{   
 187Node<k,t> newNode = new Node<k,t>(key,item,m_Head.NextNode);   
 188m_Head.NextNode = newNode;   
 189}   
 190Node<k,t> m_Head;   
 191}   
 192The linked list stores nodes:   
 193class Node<k,t>   
 194{...}   
 195Each node contains a key (of the generic type parameter K) and a value (of the generic type parameter   
 196T). Each node also has a reference to the next node in the list. The linked list itself is defined in terms of   
 197the generic type parameters K and T:   
 198public class LinkedList<k,t>   
 199{...}   
 200This allows the list to expose generic methods like AddHead():   
 201public void AddHead(K key,T item);   
 202Whenever you declare a variable of a type that uses generics, you must specify the types to use. However,   
 203the specified types can themselves be generic types. For example, the linked list has a member variable   
 204called m_Head of type Node, used for referencing the first item in the list. m_Head is declared using the   
 205list's own generic type parameters K and T   
 206Node<k,t> m_Head;   
 207You need to provide specific types when instantiating a node, and again, you can use the linked list's own   
 208generic type parameters:   
 209public void AddHead(K key,T item)   
 210{   
 211Node<k,t> newNode = new Node<k,t>(key,item,m_Head.NextNode);   
 212m_Head.NextNode = newNode;   
 213}   
 214Note that the fact the list uses the same names as the node for the generic type parameters is purely for   
 215readability purposes, and it could have used other names, such as:   
 216public class LinkedList<u,v>   
 217{...}   
 218Or:   
 219public class LinkedList<keytype,datatype>   
 220{...}   
 221In which case, m_Head would have been declared as:   
 222Node<keytype,datatype> m_Head;   
 223When the client is using the linked list, the client has to provide specific types. The client can choose   
 224integers as keys and strings as data items:   
 225LinkedList<int,string> list = new LinkedList<int,string>();   
 226list.AddHead(123,"AAA");   
 227But the client can choose any other combination, such as a time stamp for keys:   
 228LinkedList<datetime,string> list = new LinkedList<datetime,string>();   
 229list.AddHead(DateTime.Now,"AAA");   
 230Sometimes is it useful to alias a particular combination of specific types. You can do that through the   
 231using statement, as shown in Code block 4. Note that the scope of aliasing is the scope of the file, so you   
 232have to repeat aliasing across the project files in the same way you would with using namespaces.   
 233Code block 4. Generic type aliasing   
 234using List = LinkedList<int,string>;   
 235class ListClient   
 236{   
 237static void Main(string[] args)   
 238{   
 239List list = new List();   
 240list.AddHead(123,"AAA");   
 241}   
 242}   
 243Generic Constraints   
 244With C# generics, the compiler compiles the generic code into IL independent of any specific types that   
 245the clients will use. As a result, the generic code could try to use methods, properties, or members of the   
 246generic type parameters that are incompatible with the specific types the client uses. This is   
 247unacceptable because it amounts to lack of type safety. In C# you need to instruct the compiler which   
 248constraints the client-specified types must obey in order for them to be used instead of the generic type   
 249parameters. There are two types of constraints. A derivation constraint indicates to the compiler that the   
 250generic type parameter derives from a base type such an interface or a particular base class. A default   
 251constructor constraint indicates to the compiler that the generic type parameter exposes a default public   
 252constructor (a public constructor with no parameters). A generic type can employ multiple constraints,   
 253and you even get IntelliSense reflecting the constraints when using the generic type parameter, such as   
 254suggesting methods or members from the base type.   
 255It is important to note that although constraints are optional, they are often essential when developing   
 256a generic type. Without them, the compiler takes the more conservative, type-safe approach and only   
 257allows access to Object-level functionality in your generic type parameters. Constraints are part of the   
 258generic type metadata so that the client-side compiler can take advantage of them as well. The   
 259client-side compiler only allows the client developer to use types that comply with the constraints, thus   
 260enforcing type safety.   
 261An example will go a long way to explain the need and use of constraints. Suppose you would like to add   
 262indexing ability or searching by key to the linked list of Code block 3:   
 263public class LinkedList<k,t>   
 264{   
 265T Find(K key)   
 266{...}   
 267public T this[K key]   
 268{   
 269get{return Find(key);}   
 270}   
 271}   
 272This allows the client to write the following code:   
 273LinkedList<int,string> list = new LinkedList<int,string>();   
 274list.AddHead(123,"AAA");   
 275list.AddHead(456,"BBB");   
 276string item = list[456];   
 277Debug.Assert(item == "BBB");   
 278To implement the search, you need to scan the list, compare each node's key with the key you're looking   
 279for, and return the item of the node whose key matches. The problem is that the following   
 280implementation of Find() does not compile:   
 281T Find(K key)   
 282{   
 283Node<k,t> current = m_Head;   
 284while(current.NextNode != null)   
 285{   
 286if(current.Key == key) //Will not compile   
 287break;   
 288else   
 289current = current.NextNode;   
 290}   
 291return current.Item;   
 292}   
 293The reason is that the compiler will refuse to compile this line:   
 294if(current.Key == key)   
 295The line above will not compile because the compiler does not know whether K (or the actual type   
 296supplied by the client) supports the == operator. For example, structs by default do not provide such an   
 297implementation. You could try to overcome the == operator limitation by using the IComparable   
 298interface:   
 299public interface IComparable   
 300{   
 301int CompareTo(object obj);   
 302}   
 303CompareTo() returns 0 if the object you compare to is equal to the object implementing the interface,   
 304so the Find() method could use it as follows:   
 305if(current.Key.CompareTo(key) == 0)   
 306Unfortunately, this does not compile either because the compiler has no way of knowing whether K (or   
 307the actual type supplied by the client) is derived from IComparable.   
 308You could explicitly cast to IComparable to force the compiler to compile the comparing line, except   
 309doing so is at the expense of type safety:   
 310if(((IComparable)(current.Key)).CompareTo(key) == 0)   
 311If the type the client uses does not derive from IComparable, it results in a run-time exception. In   
 312addition, when the key type used is a value type instead of the key type parameter, you force a boxing   
 313of the key, and that may have some performance implications.   
 314Derivation Constraints   
 315In C# 2.0 you use the where reserved keyword to define a constraint. Use the where keyword on the   
 316generic type parameter followed by a derivation colon to indicate to the compiler that the generic type   
 317parameter implements a particular interface. For example, here is the derivation constraint required to   
 318implement the Find() method of LinkedList:   
 319public class LinkedList<k,t> where K : IComparable   
 320{   
 321T Find(K key)   
 322{   
 323Node<k,t> current = m_Head;   
 324while(current.NextNode != null)   
 325{   
 326if(current.Key.CompareTo(key) == 0)   
 327break;   
 328else   
 329current = current.NextNode;   
 330}   
 331return current.Item;   
 332}   
 333//Rest of the implementation   
 334}   
 335Note that even though the constraint allows you to use IComparable, it does not eliminate the boxing   
 336penalty when the key used is a value type, such as an integer. To overcome this, the   
 337System.Collections.Generics namespace defines the generic interface IComparable<t>:   
 338public interface IComparable<t>   
 339{   
 340int CompareTo(T other);   
 341}   
 342You can constrain the key type parameter to support IComparable<t> with the key's type as the type   
 343parameter, and by doing so you gain not only type safety but also eliminate the boxing of value types   
 344when used as keys:   
 345public class LinkedList<k,t> where K : IComparable<k>   
 346{...}   
 347In fact, all the types that supported IComparable in .NET 1.1 support IComparable<t> in .NET 2.0.   
 348This enables the use for keys of common types such as int, string, Guid, DateTime, and so on.   
 349In C# 2.0, all constraints must appear after the actual derivation list of the generic class. For example,   
 350if LinkedList derives from the IEnumerable<t> interface (for iterator support), you would put the   
 351where keyword immediately after it:   
 352public class LinkedList<k,t> : IEnumerable<t> where K : IComparable<k>   
 353{...}   
 354Note that the constraint appears where the generic type parameter K is defined, at the declaration line of   
 355the LinkedList class. This permits the class LinkedList to treat the generic type parameter K as   
 356implementing IComparable<t> with K as the type parameter. You will also get IntelliSense support on   
 357the methods of interfaces you constrain.   
 358When the client declares a variable of type LinkedList providing a concrete type for the list's key, the   
 359client-side compiler will insist that the key type is derived from IComparable<t> (with the key's type   
 360as the type parameter) and will refuse to build the client code otherwise.   
 361In general, only define a constraint at the level where you require it. In the linked list example, it is   
 362pointless to define the IComparable<t> derivation constraint at the node level, because the node itself   
 363does not compare keys. If you do so, you will have to place the constraint at the LinkedList level as well,   
 364even if the list does not compare keys. This is because the list contains a node as a member variable,   
 365causing the compiler to insist that the key type defined at the list level complies with the constraint   
 366placed by the node on the generic key type.   
 367In other words, if you define the node as so:   
 368class Node<k,t> where K : IComparable<k>   
 369{...}   
 370Then you would have to repeat the constraint at the list level, even if you do not provide the Find()   
 371method, or any other method for that matter:   
 372public class LinkedList<keytype,datatype> where KeyType :   
 373IComparable<keytype>   
 374{   
 375Node<keytype,datatype> m_Head;   
 376}   
 377You can constrain multiple interfaces on the same generic type parameter, separated by a comma. For   
 378example:   
 379public class LinkedList<k,t> where K : IComparable<k>,IConvertible   
 380{...}   
 381You can provide constraints for every generic type parameter your class uses, for example:   
 382public class LinkedList<k,t> where K : IComparable<k>   
 383where T : ICloneable   
 384{...}   
 385You can have a base class constraint, meaning, stipulating that the generic type parameter is derived   
 386from a particular base class:   
 387public class MyBaseClass   
 388{...}   
 389public class LinkedList<k,t> where K : MyBaseClass   
 390{...}   
 391However, at most one base class can be used in a constraint because C# does not support multiple   
 392inheritance of implementation. Obviously, the base class you constrain cannot be a sealed class, and the   
 393compiler enforces that. In addition, you cannot constrain System.Delegate or System.Array as a base   
 394class .   
 395You can constrain both a base class and one or more interfaces, but the base class must appear first in   
 396the derivation constraint list:   
 397public class LinkedList<k,t> where K : MyBaseClass, IComparable<k>   
 398{...}   
 399C# does not allow you to specify a naked generic type parameter as a constraint:   
 400public class LinkedList<k,t,u> where K : U //Will not compile   
 401{...}   
 402However, C# does let you use another generic type as a constraint:   
 403public interface ISomeInterface<t>   
 404{...}   
 405public class LinkedList<k,t> where K : ISomeInterface<int>   
 406{...}   
 407When constraining anther generic type as a base type, you can keep that type generic by specifying the   
 408generic type parameters of your own type parameter. For example, in the case of a generic interface   
 409constraint:   
 410public class LinkedList<k,t> where K : ISomeInterface<t>   
 411{...}   
 412Or a generic base class constraint:   
 413public class MySubClass<t> where T : MyBaseClass<t>   
 414{...}   
 415Finally, note that when providing a derivation constraint, the base type (interface or base class) you   
 416constrain must have consistent visibility with that of the generic type parameter you define. For instance,   
 417the following constraint is valid, because internal types can use public types:   
 418public class MyBaseClass   
 419{}   
 420internal class MySubClass<t> where T : MyBaseClass   
 421{}   
 422However, if the visibility of the two classes were reversed, such as:   
 423internal class MyBaseClass   
 424{}   
 425public class MySubClass<t> where T : MyBaseClass   
 426{}   
 427Then the compiler would issue an error, because no client from outside the assembly will ever be able to   
 428use the generic type MySubClass, rendering MySubClass in effect as an internal rather than a public   
 429type. The reason outside clients cannot use MySubClass is that to declare a variable of type   
 430MySubClass, they require to make use of a type that derives from the internal type MyBaseClass.   
 431Constructor Constraint   
 432Suppose you want to instantiate a new generic object inside a generic class. The problem is the C#   
 433compiler does not know whether the specific type the client will use has a matching constructor, and it   
 434will refuse to compile the instantiation line.   
 435To address this problem, C# allows you to constrain a generic type parameter such that it must support   
 436a public default constructor. This is done using the new() constraint. For example, here is a different way   
 437of implementing the default constructor of the generic Node <k,t> from Code block 3.   
 438class Node<k,t> where T : new()   
 439{   
 440public Node()   
 441{   
 442Key = K.default;   
 443Item = new T();   
 444NextNode = null;   
 445}   
 446public K Key;   
 447public T Item;   
 448public Node<k,t> NextNode;   
 449}   
 450You can combine the constructor constraint with derivation constraint, provided the constructor   
 451constraint appears last in the constraint list:   
 452public class LinkedList<k,t> where K : IComparable<k>,new()   
 453{...}   
 454Generics and Casting   
 455The C# compiler only lets you implicitly cast generic type parameters to Object, or to constraint-specified   
 456types, as shown in Code block 5. Such implicit casting is type safe because any incompatibility is   
 457discovered at compile-time.   
 458Code block 5. Implicit casting of generic type parameters   
 459interface ISomeInterface   
 460{...}   
 461class BaseClass   
 462{...}   
 463class MyClass<t> where T : BaseClass,ISomeInterface   
 464{   
 465void SomeMethod(T t)   
 466{   
 467ISomeInterface obj1 = t;   
 468BaseClass obj2 = t;   
 469object obj3 = t;   
 470}   
 471}   
 472The compiler lets you explicitly cast generic type parameters to any other interface, but not to a class:   
 473interface ISomeInterface   
 474{...}   
 475class SomeClass   
 476{...}   
 477class MyClass<t>   
 478{   
 479void SomeMethod(T t)   
 480{   
 481ISomeInterface obj1 = (ISomeInterface)t;//Compiles   
 482SomeClass obj2 = (SomeClass)t; //Does not compile   
 483}   
 484}   
 485However, you can force a cast from a generic type parameter to any other type using a temporary Object   
 486variable:   
 487class SomeClass   
 488{...}   
 489class MyClass<t>   
 490{   
 491void SomeMethod(T t)   
 492{   
 493object temp = t;   
 494SomeClass obj = (SomeClass)temp;   
 495}   
 496}   
 497Needless to say, such explicit casting is dangerous because it may throw an exception at run time if the   
 498concrete type used instead of the generic type parameter does not derive from the type to which you   
 499explicitly cast. Instead of risking a casting exception, a better approach is to use the is and as operators,   
 500as shown in Code block 6. The is operator returns true if the generic type parameter is of the queried   
 501type, and as will perform a cast if the types are compatible, and will return null otherwise. You can use   
 502is and as on both naked generic type parameters and on generic classes with specific parameters.   
 503Code block 6. Using 'is' and 'as' operators on generic type parameters   
 504public class MyClass<t>   
 505{   
 506public void SomeMethod(T t)   
 507{   
 508if(t is int)   
 509{...}   
 510if(t is LinkedList<int,string>)   
 511{...}   
 512string str = t as string;   
 513if(str != null)   
 514{...}   
 515LinkedList<int,string> list = t as LinkedList<int,string>;   
 516if(list != null)   
 517{...}   
 518}   
 519}   
 520Inheritance and Generics   
 521When deriving from a generic base class, you must provide a specific type instead of the base-class's   
 522generic type parameter:   
 523public class BaseClass<t>   
 524{...}   
 525public class SubClass : BaseClass<int>   
 526{...}   
 527If the subclass is generic, instead of a concrete type, you can use the subclass generic type parameter as   
 528the specified type for the generic base class:   
 529public class SubClass<t> : BaseClass<t>   
 530{...}   
 531When using the subclass generic type parameters, you must repeat any constraints stipulated at the   
 532base class level at the subclass level. For example, derivation constraint:   
 533public class BaseClass<t> where T : ISomeInterface   
 534{...}   
 535public class SubClass<t> : BaseClass<t> where T : ISomeInterface   
 536{...}   
 537Or constructor constraint:   
 538public class BaseClass<t> where T : new()   
 539{   
 540public T SomeMethod()   
 541{   
 542return new T();   
 543}   
 544}   
 545public class SubClass<t> : BaseClass<t> where T : new()   
 546{...}   
 547A base class can define virtual methods whose signatures use generic type parameters. When overriding   
 548them, the subclass must provide the corresponding types in the method signatures:   
 549public class BaseClass<t>   
 550{   
 551public virtual T SomeMethod()   
 552{...}   
 553}   
 554public class SubClass: BaseClass<int>   
 555{   
 556public override int SomeMethod()   
 557{...}   
 558}   
 559If the subclass is generic it can also use its own generic type parameters for the override:   
 560public class SubClass<t>: BaseClass<t>   
 561{   
 562public override T SomeMethod()   
 563{...}   
 564}   
 565You can define generic interfaces, generic abstract classes, and even generic abstract methods. These   
 566types behave like any other generic base type:   
 567public interface ISomeInterface<t>   
 568{   
 569T SomeMethod(T t);   
 570}   
 571public abstract class BaseClass<t>   
 572{   
 573public abstract T SomeMethod(T t);   
 574}   
 575public class SubClass<t> : BaseClass<t>   
 576{   
 577public override T SomeMethod(T t)   
 578{...)   
 579}   
 580There is an interesting use for generic abstract methods and generic interfaces. In C# 2.0, it is impossible   
 581to use operators such as + or += on generic type parameters. For example, the following code does not   
 582compile because C# 2.0 does not have operator constraints:   
 583public class Calculator<t>   
 584{   
 585public T Add(T arg1,T arg2)   
 586{   
 587return arg1 + arg2;//Does not compile   
 588}   
 589//Rest of the methods   
 590}   
 591Nonetheless, you can compensate using abstract methods (or preferably interfaces) by defining generic   
 592operations. Since an abstract method cannot have any code in it, you can specify the generic operations   
 593at the base class level, and provide a concrete type and implementation at the subclass level:   
 594public abstract class BaseCalculator<t>   
 595{   
 596public abstract T Add(T arg1,T arg2);   
 597public abstract T Subtract(T arg1,T arg2);   
 598public abstract T Divide(T arg1,T arg2);   
 599public abstract T Multiply(T arg1,T arg2);   
 600}   
 601public class MyCalculator : BaseCalculator<int>   
 602{   
 603public override int Add(int arg1, int arg2)   
 604{   
 605return arg1 + arg2;   
 606}   
 607//Rest of the methods   
 608}   
 609A generic interface will yield a somewhat cleaner solution as well:   
 610public interface ICalculator<t>   
 611{   
 612T Add(T arg1,T arg2);   
 613//Rest of the methods   
 614}   
 615public class MyCalculator : ICalculator<int>   
 616{   
 617public int Add(int arg1, int arg2)   
 618{   
 619return arg1 + arg2;   
 620}   
 621//Rest of the methods   
 622}   
 623Generic Methods   
 624In C# 2.0, a method can define generic type parameters, specific to its execution scope:   
 625public class MyClass<t>   
 626{   
 627public void MyMethod<x>(X x)   
 628{...}   
 629}   
 630This is an important capability because it allows you to call the method with a different type every time,   
 631which is very handy for utility classes.   
 632You can define method-specific generic type parameters even if the containing class does not use   
 633generics at all:   
 634public class MyClass   
 635{   
 636public void MyMethod<t>(T t)   
 637{...}   
 638}   
 639This ability is for methods only. Properties or indexers can only use generic type parameters defined at   
 640the scope of the class.   
 641When calling a method that defines generic type parameters, you can provide the type to use at the call   
 642site:   
 643MyClass obj = new MyClass();   
 644obj.MyMethod<int>(3);   
 645That said, when the method is invoked the C# compiler is smart enough to infer the correct type based   
 646on the type of parameter passed in, and it allows omitting the type specification altogether:   
 647MyClass obj = new MyClass();   
 648obj.MyMethod(3);   
 649This ability is called generic type inference. Note that the compiler cannot infer the type based on the   
 650type of the returned value alone:   
 651public class MyClass   
 652{   
 653public T MyMethod<t>()   
 654{}   
 655}   
 656MyClass obj = new MyClass();   
 657int number = obj.MyMethod();//Does not compile   
 658When a method defines its own generic type parameters, it can also define constraints for these types:   
 659public class MyClass   
 660{   
 661public void SomeMethod<t>(T t) where T : IComparable<t>   
 662{...}   
 663}   
 664However, you cannot provide method-level constraints for class-level generic type parameters. All   
 665constraints for class-level generic type parameters must be defined at the class scope.   
 666When overriding a virtual method that defines generic type parameters, the subclass method must   
 667redefine the method-specific generic type parameter:   
 668public class BaseClass   
 669{   
 670public virtual void SomeMethod<t>(T t)   
 671{...}   
 672}   
 673public class SubClass : BaseClass   
 674{   
 675public override void SomeMethod<t>(T t)   
 676{...}   
 677}   
 678The subclass implementation must repeat all constraints that appeared at the base method level:   
 679public class BaseClass   
 680{   
 681public virtual void SomeMethod<t>(T t) where T : new()   
 682{...}   
 683}   
 684public class SubClass : BaseClass   
 685{   
 686public override void SomeMethod<t>(T t) where T : new()   
 687{...}   
 688}   
 689Note that the method override cannot define new constraints that did not appear at the base method.   
 690In addition, if the subclass method calls the base class implementation of the virtual method, it must   
 691specify the type to use instead of the generic base method type parameter. You can either explicitly   
 692specify it yourself or rely on type inference if it is available:   
 693public class BaseClass   
 694{   
 695public virtual void SomeMethod<t>(T t)   
 696{...}   
 697}   
 698public class SubClass : BaseClass   
 699{   
 700public override void SomeMethod<t>(T t)   
 701{   
 702base.SomeMethod<t>(t);   
 703base.SomeMethod(t);   
 704}   
 705}   
 706Generic Static Method   
 707C# allows you to define static methods that use generic type parameters. However, when invoking such   
 708a static method, you need to provide the concrete type for the containing class at the call site, such as in   
 709this example:   
 710public class MyClass<t>   
 711{   
 712public static T SomeMethod(T t)   
 713{...}   
 714}   
 715int number = MyClass<int>.SomeMethod(3);   
 716Static methods can define method-specific generic type parameters and constraints, similar to instance   
 717methods. When calling such methods, you need to provide the method-specific types at the call site,   
 718either explicitly:   
 719public class MyClass<t>   
 720{   
 721public static T SomeMethod<x>(T t,X x)   
 722{..}   
 723}   
 724int number = MyClass<int>.SomeMethod<string>(3,"AAA");   
 725Or rely on type inference when possible:   
 726int number = MyClass<int>.SomeMethod(3,"AAA");   
 727Generic static methods are subjected to all constraints imposed on the generic type parameter they use   
 728at the class level. As with instance method, you can provide constraints for generic type parameters   
 729defined by the static method:   
 730public class MyClass   
 731{   
 732public static T SomeMethod<t>(T t) where T : IComparable<t>   
 733{...}   
 734}   
 735Operators in C# are nothing more than static methods and C# allows you to overload operators for your   
 736generic types. Imagine that the generic LinkedList of Code block 3 provided the + operator for   
 737concatenating linked lists. The + operator enables you to write the following elegant code:   
 738LinkedList<int,string> list1 = new LinkedList<int,string>();   
 739LinkedList<int,string> list2 = new LinkedList<int,string>();   
 740...   
 741LinkedList<int,string> list3 = list1+list2;   
 742Code block 7 shows the implementation of the generic + operator on the LinkedList class. Note that   
 743operators cannot define new generic type parameters.   
 744Code block 7. Implementing a generic operator   
 745public class LinkedList<k,t>   
 746{   
 747public static LinkedList<k,t> operator+(LinkedList<k,t> lhs,   
 748LinkedList<k,t> rhs)   
 749{   
 750return concatenate(lhs,rhs);   
 751}   
 752static LinkedList<k,t> concatenate(LinkedList<k,t> list1,   
 753LinkedList<k,t> list2)   
 754{   
 755LinkedList<k,t> newList = new LinkedList<k,t>();   
 756Node<k,t> current;   
 757current = list1.m_Head;   
 758while(current != null)   
 759{   
 760newList.AddHead(current.Key,current.Item);   
 761current = current.NextNode;   
 762}   
 763current = list2.m_Head;   
 764while(current != null)   
 765{   
 766newList.AddHead(current.Key,current.Item);   
 767current = current.NextNode;   
 768}   
 769return newList;   
 770}   
 771//Rest of LinkedList   
 772}   
 773Generic Delegates   
 774A delegate defined in a class can take advantage of the generic type parameter of that class. For   
 775example:   
 776public class MyClass<t>   
 777{   
 778public delegate void GenericDelegate(T t);   
 779public void SomeMethod(T t)   
 780{...}   
 781}   
 782When specifying a type for the containing class, it affects the delegate as well:   
 783MyClass<int> obj = new MyClass<int>();   
 784MyClass<int>.GenericDelegate del;   
 785del = new MyClass<int>.GenericDelegate(obj.SomeMethod);   
 786del(3);   
 787C# 2.0 allows you to make a direct assignment of a method reference into a delegate variable:   
 788MyClass<int> obj = new MyClass<int>();   
 789MyClass<int>.GenericDelegate del;   
 790del = obj.SomeMethod;   
 791I am calling this feature delegate inference. The compiler is capable of inferring the type of the delegate   
 792you assign into, finding if the target object has a method by the name you specify, and verifying that the   
 793method's signature matches. Then, the compiler creates a new delegate of the inferred argument type   
 794(including the correct type instead of the generic type parameter), and assigns the new delegate into the   
 795inferred delegate.   
 796Like classes, structs, and methods, delegates can define generic type parameters too:   
 797public class MyClass<t>   
 798{   
 799public delegate void GenericDelegate<x>(T t,X x);   
 800}   
 801Delegates defined outside the scope of a class can use generic type parameters. In that case, you have   
 802to provide the specific types for the delegate when declaring and instantiating it:   
 803public delegate void GenericDelegate<t>(T t);   
 804public class MyClass   
 805{   
 806public void SomeMethod(int number)   
 807{...}   
 808}   
 809MyClass obj = new MyClass();   
 810GenericDelegate<int> del;   
 811del = new GenericDelegate<int>(obj.SomeMethod);   
 812del(3);   
 813Alternatively, you can use delegate inference when assigning the delegate:   
 814MyClass obj = new MyClass();   
 815GenericDelegate<int> del;   
 816del = obj.SomeMethod;   
 817And naturally, a delegate can define constraints to accompany its generic type parameters:   
 818public delegate void MyDelegate<t>(T t) where T : IComparable<t>;   
 819The delegate-level constraints are enforced only on the using side, when declaring a delegate variable   
 820and instantiating a delegate object, similar to any other constraint at the scope of types or methods.   
 821Generic delegates are especially useful when it comes to events. You can literally define a limited set of   
 822generic delegates, distinguished only by the number of the generic type parameters they require, and   
 823use these delegates for all of your event handling needs. Code block 8 demonstrates the use of a   
 824generic delegate and a generic event-handling method.   
 825Code block 8. Generic event handling   
 826public delegate void   
 827GenericEventHandler<sendertype,argstype>(SenderType sender,   
 828ArgsType args);   
 829public class MyPublisher   
 830{   
 831public event GenericEventHandler<mypublisher,eventargs> MyEvent;   
 832public void FireEvent()   
 833{   
 834MyEvent(this,EventArgs.Empty);   
 835}   
 836}   
 837public class MySubscriber<argstype> //Optional: can be a specific type   
 838{   
 839public void SomeMethod(MyPublisher sender,ArgsType args)   
 840{...}   
 841}   
 842MyPublisher publisher = new MyPublisher();   
 843MySubscriber<eventargs> subs = new MySubscriber<eventargs>();   
 844publisher.MyEvent += subs.SomeMethod;   
 845Code block 8 uses a generic delegate called GenericEventHandler, which accepts a generic sender   
 846type and a generic type parameter. Obviously, if you need more parameters, you can simply add more   
 847generic type parameters, but I wanted to model GenericEventHandler after the non-generic .NET   
 848EventHandler, defined as:   
 849public void delegate EventHandler(object sender,EventArgs args);   
 850Unlike EventHandler, GenericEventHandler is type safe, as shown in Code block 8 because it   
 851accepts only objects of the type MyPublisher as senders, rather than mere Object.   
 852Generics and Reflection   
 853In .NET 2.0, reflection is extended to support generic type parameters. The type Type can now represent   
 854generic types with specific type parameters (called bounded types), or unspecified (unbounded) types.   
 855As in C# 1.1, you can obtain the Type of any type by using the typeof operator or by calling the   
 856GetType() method that every type supports. Regardless of the way you choose, both yield the same   
 857Type. For example, in the following code sample, type1 is identical to type2.   
 858LinkedList<int,string> list = new LinkedList<int,string>();   
 859Type type1 = typeof(LinkedList<int,string>);   
 860Type type2 = list.GetType();   
 861Debug.Assert(type1 == type2);   
 862Both typeof and GetType() can operate on naked generic type parameters:   
 863public class MyClass<t>   
 864{   
 865public void SomeMethod(T t)   
 866{   
 867Type type = typeof(T);   
 868Debug.Assert(type == t.GetType());   
 869}   
 870}   
 871Type has new methods and properties designed to provide reflection information about the generic   
 872aspect of the type. Code block 9 shows the new methods.   
 873Code block 9. Type's generic reflection members   
 874public abstract class Type : //Base types   
 875{   
 876public int GenericParameterPosition{virtual get;}   
 877public bool HasGenericParameters{get;}   
 878public bool HasUnboundGenericParameters{virtual get;}   
 879public bool IsGenericParameter{virtual get;}   
 880public bool IsGenericTypeDefinition{virtual get;}   
 881public virtual Type BindGenericParameters(Type[] typeArgs);   
 882public virtual Type[] GetGenericParameters();   
 883public virtual Type GetGenericTypeDefinition();   
 884//Rest of the members   
 885}   
 886The most useful of these new members are the HasGenericParameters and   
 887HasUnboundGenericParameters properties, and GetGenericParameters() and   
 888GetGenericTypeDefinition() methods. The rest of Type's new members are for advanced and   
 889somewhat esoteric scenarios beyond the scope of this article. As its name indicates,   
 890HasGenericParameters is set to true if the type represented by the Type object uses generic type   
 891parameters. GetGenericParameters() returns an array of Types corresponding to the bounded types   
 892used. GetGenericTypeDefinition() returns a Type representing the generic form of the underlying   
 893type. HasUnboundGenericParameters is true if the Type object has any unspecified generic type   
 894parameters. It follows that HasUnboundGenericParameters is set to true on the Type returned from   
 895GetGenericTypeDefinition() when called on a generic type.   
 896Code block 10 demonstrates using these new Type members to obtain generic reflection information on   
 897the LinkedList from Code block 3.   
 898Code block 10. Using Type for generic reflection   
 899LinkedList<int,string> list = new LinkedList<int,string>();   
 900Type boundedType = list.GetType();   
 901Trace.WriteLine(boundedType.ToString());   
 902//Writes 'LinkedList[System.Int32,System.String]'   
 903Debug.Assert(boundedType.HasGenericParameters);   
 904Type[] parameters = boundedType.GetGenericParameters();   
 905Debug.Assert(parameters.Length == 2);   
 906Debug.Assert(parameters[0] == typeof(int));   
 907Debug.Assert(parameters[1] == typeof(string));   
 908Type unboundedType = boundedType.GetGenericTypeDefinition();   
 909Debug.Assert(unboundedType.HasUnboundGenericParameters);   
 910Trace.WriteLine(unboundedType.ToString());   
 911//Writes 'LinkedList[K,T]'   
 912As shown in Code block 10, a Type can refer to a generic type with bounded parameters   
 913(boundedType in Code block 10), or to a generic type with unbounded parameters (unboundedType   
 914in Code block 10).   
 915Similar to Type, MethodInfo and its base class MethodBase have new members that reflect generic   
 916method information.   
 917As in C# 1.1, you can use MethodInfo (as well as a number of other options) for late binding invocation.   
 918However, the type of the parameters you pass for the late binding must match the bounded types used   
 919instead of the generic type parameters (if any):   
 920LinkedList<int,string> list = new LinkedList<int,string>();   
 921Type type = list.GetType();   
 922MethodInfo methodInfo = type.GetMethod("AddHead");   
 923object[] args = {1,"AAA"};   
 924methodInfo.Invoke(list,args);   
 925Attributes and Generics   
 926When defining an attribute, you can instruct the compiler that the attribute should target generic type   
 927parameters, using the new GenericParameter value of the enum AttributeTargets:   
 928[AttributeUsage(AttributeTargets.GenericParameter)]   
 929public class SomeAttribute : Attribute   
 930{...}   
 931Note that C# 2.0 does not allow you to define generic attributes.   
 932//Does not compile:   
 933public class SomeAttribute<t> : Attribute   
 934{...}   
 935Yet internally, an attribute class can take advantage of generics by using generic types or define helper   
 936generic methods, like any other type:   
 937public class SomeAttribute : Attribute   
 938{   
 939void SomeMethod<t>(T t)   
 940{...}   
 941LinkedList<int,string> m_List = new LinkedList<int,string>();   
 942}   
 943Generics and the .NET Framework   
 944To conclude this article, here are how some other areas in .NET besides C# itself are taking advantage of   
 945or interacting with generics.   
 946Generic Collections   
 947The data structures in System.Collections are all Object-based, and thus inheriting the two problems   
 948described at the beginning of this article, namely, poor performance and lack of type safety. .NET 2.0   
 949introduces a new set of generic collections in the System.Collections.Generics namespace. For example,   
 950there are generic Stack<t> and a generic Queue<t> classes. The Dictionary<k,t> data structure is   
 951equivalent to the non-generic HashTable, and there is also a SortedDictionary<k,t> class somewhat   
 952like SortedList. The class List<t> is analogous to the non-generic ArrayList. Table 1 maps the new   
 953types of System.Collections.Generics to those of System.Collections.   
 954Table 1. Mapping System.Collections.Generics to System.Collections   
 955System.Collections.Generics System.Collections   
 956Comparer<t> Comparer   
 957Dictionary<k,t> HashTable   
 958List<t> ArrayList   
 959Queue<t> Queue   
 960SortedDictionary<k,t> SortedList   
 961Stack<t> Stack   
 962ICollection<t> ICollection   
 963IComparable<t> System.IComparable   
 964IComparer<t> IComparer   
 965IDictionary<k,t> IDictionary   
 966IEnumerable<t> IEnumerable   
 967IEnumerator<t> IEnumerator   
 968IKeyComparer<t> IKeyComparer   
 969IList<t> IList   
 970All the generic collections in System.Collections.Generics also implement the generic IEnumerable<t>   
 971interface, defined as:   
 972public interface IEnumerable<t>   
 973{   
 974IEnumerator<t> GetEnumerator();   
 975}   
 976public interface IEnumerator<t> : IDisposable   
 977{   
 978T Current{get;}   
 979bool MoveNext();   
 980}   
 981Briefly, IEnumerable<t> provides access to the IEnumerator<t> iterator interface, used for   
 982abstracted iterations over the collection. All the collections implement IEnumerable<t> on a nested   
 983struct, where the generic type parameter T is the type the collection stores.   
 984Of particular interest is the way the dictionary collections define their iterators. A dictionary is actually a   
 985collection of not one but two types of generic parameterthe key and the value.   
 986System.Collection.Generics provides a generic struct called KeyValuePair<k,v> defined as:   
 987struct KeyValuePair<k,v>   
 988{   
 989public KeyValuePair(K key,V value);   
 990public K Key(get;set;)   
 991public V Value(get;set;)   
 992}   
 993KeyValuePair<k,v> simply stores a pair of a generic key and a generic value, and in doing so, defines in   
 994effect a new generic type. This new type is what the dictionary manages as a collection, and what it uses   
 995for its implementation of IEnumerable<t>. The Dictionary class specifies the generic   
 996KeyValuePair<k,v> structure as the item type for IEnumerable<t> and ICollection<t>:   
 997public class Dictionary<k,t> : IEnumerable<keyvaluepair<k,t>&gt;,   
 998ICollection<keyvaluepair<k,t>&gt;,   
 999//More interfaces   
1000{...}   
1001The key and value type parameters used in KeyValuePair<k,v> are of course the dictionary's own   
1002generic key and value type parameters. I am calling this technique compound generics.   
1003You can certainly take advantage of compound generics in your own generic data structures that use   
1004pairs of keys and values. For example:   
1005public class LinkedList<k,t> : IEnumerable<keyvaluepair<k,t>&gt; where K :   
1006IComparable<k>   
1007{...}   
1008Serialization and Generics   
1009.NET allows you to have serializable generic types:   
1010[Serializable]   
1011public class MyClass<t>   
1012{...}   
1013When serializing a type, besides the state of the object members, .NET persists metadata about the   
1014object and its type. If the serializable type is generic and it contains bounded types, the metadata about   
1015the generic type contains type information about the bounded types as well. Consequently, each   
1016permutation of a generic type with a specific argument type is considered a unique type. For example,   
1017you cannot serialize an object type MyClass<int> but deserialize it into an object of type   
1018MyClass<string>. Serializing an instance of a generic type is no different from serializing a non-generic   
1019type. However, when deserializing that type, you need to declare a variable with matching specific types,   
1020and specify these types again when down casting the Object returned from Deserialize. Code block 11   
1021shows serialization and deserialization of a generic type.   
1022Code block 11. Client-side serialization of a generic type   
1023[Serializable]   
1024public class MyClass<t>   
1025{...}   
1026MyClass<int> obj1 = new MyClass<int>();   
1027IFormatter formatter = new BinaryFormatter();   
1028Stream stream = new   
1029FileStream("obj.bin",FileMode.Create,FileAccess.ReadWrite);   
1030formatter.Serialize(stream,obj1);   
1031stream.Seek(0,SeekOrigin.Begin);   
1032MyClass<int> obj2;   
1033obj2 = (MyClass<int>)formatter.Deserialize(stream);   
1034stream.Close();   
1035When providing custom serialization, you have to add values to a property bag called SerializationInfo,   
1036which you also get values from during deserialization. You implement the interface ISerializable with   
1037the single method GetObjectData() that provides the SerializationInfo used in serialization. You also   
1038must implement a special constructor that accepts the SerializationInfo from which to get the object   
1039state.   
1040SerializationInfo provides methods for getting or adding field values. Each field is identified by a string.   
1041SerializationInfo has type-safe methods for most of the CLR basic types, such as int and string:   
1042public sealed class SerializationInfo   
1043{   
1044public void AddValue(string name, int value);   
1045public int GetInt32(string name);   
1046//Other methods and properties   
1047}   
1048The problem when providing custom serialization on a generic type is that you do not know which of the   
1049adding or getting methods to use. To address this issue, SerializationInfo provides methods to add or   
1050get an Object and its type:   
1051public void AddValue(string name, object value, Type type);   
1052public object GetValue(string name,Type type);   
1053Note These methods are available in .NET 1.1 as well.   
1054When using AddValue(), obtain the type of the generic parameter type. When calling GetValue(), use   
1055a cast to the generic parameter type because GetValue() returns an Object. Code block 12   
1056demonstrates the use of these AddValue() and GetValue() methods.   
1057Code block 12. Custom serialization of a generic class   
1058[Serializable]   
1059public class MyClass<t> : ISerializable   
1060{   
1061public MyClass()   
1062{}   
1063public void GetObjectData(SerializationInfo info,StreamingContext   
1064ctx)   
1065{   
1066info.AddValue("m_T",m_T,typeof(T));   
1067}   
1068private MyClass(SerializationInfo info,StreamingContext context)   
1069{   
1070m_T = (T)info.GetValue("m_T",typeof(T));   
1071}   
1072T m_T;   
1073}   
1074However, there is a better, safer way for providing custom serialization on a generic type. Code block   
107513 presents the GenericSerializationInfo utility class that exposes generic AddValue() and   
1076GetValue() methods. By encapsulating a regular SerializationInfo, GenericSerializationInfo   
1077shields the client code from the type retrieval and explicit casting.   
1078Code block 13. The GenericSerializationInfo utility class.   
1079public class GenericSerializationInfo   
1080{   
1081SerializationInfo m_SerializationInfo;   
1082public GenericSerializationInfo(SerializationInfo info)   
1083{   
1084m_SerializationInfo = info;   
1085}   
1086public void AddValue<t>(string name,T value)   
1087{   
1088m_SerializationInfo.AddValue(name,value,value.GetType());   
1089}   
1090public T GetValue<t>(string name)   
1091{   
1092object obj = m_SerializationInfo.GetValue(name,typeof(T));   
1093return (T)obj;   
1094}   
1095}   
1096Code block 14 shows the same custom serialization code as in Code block 12, except it uses   
1097GenericSerializationInfo. Note the use of type inference in the call to AddValue().   
1098Code block 14. Using GenericSerializationInfo   
1099[Serializable]   
1100public class MyClass<t> : ISerializable   
1101{   
1102public MyClass()   
1103{}   
1104public void GetObjectData(SerializationInfo info,StreamingContext   
1105ctx)   
1106{   
1107GenericSerializationInfo genericInfo = new   
1108GenericSerializationInfo(info);   
1109genericInfo.AddValue("m_T",m_T); //using type inference   
1110}   
1111private MyClass(SerializationInfo info,StreamingContext context)   
1112{   
1113GenericSerializationInfo genericInfo = new   
1114GenericSerializationInfo(info);   
1115m_T = genericInfo.GetValue<t>("m_T");   
1116}   
1117T m_T;   
1118}   
1119I use GenericSerializationInfo as a cleaner way of implementing custom serialization, even on   
1120non-generic class members. For example, if MyClass in Code block 14 had a class member of type   
1121string called m_Name, you could write:   
1122genericInfo.AddValue("m_Name",m_Name);   
1123and:   
1124m_Name = genericInfo.GetValue<string>("m_Name");   
1125Generics and Remoting   
1126You can define and deploy remote classes that utilize generics, and you can use programmatic or   
1127administrative configuration. Consider the class MyServer that uses generics and is derived from   
1128MarshalByRefObject:   
1129public class MyServer<t> : MarshalByRefObject   
1130{...}   
1131When using administrative type registration, you need to specify the exact types to use instead of the   
1132generic type parameters. You must name the types in a language-neutral manner, and provide fully   
1133qualified namespaces. For example, suppose the class MyServer is defined in the namespace   
1134RemoteServer in the assembly ServerAssembly, and you want to use it with integer instead of the   
1135generic type parameter T, in client activated mode. In that case, the required client-side type registration   
1136entry in the configuration file would be:   
1137<client url="...some url goes here...">
1138<activated type="RemoteServer.MyServer[[System.Int32]],ServerAssembly"></activated>
1139</client>   
1140The matching host-side type registration entry in the configuration file is:   
1141<service>
1142<activated type="RemoteServer.MyServer[[System.Int32]],ServerAssembly"></activated>
1143</service>   
1144The double square brackets are used to specify multiple types. For example:   
1145LinkedList[[System.Int32],[System.String]]   
1146When using programmatic configuration, you configure activation modes and type registration similar to   
1147C# 1.1, except when defining the type of the remote object you have to provide specific types instead of   
1148the generic type parameters. For example, for host-side activation mode and type registration you would   
1149write:   
1150Type serverType = typeof(MyServer<int>);   
1151RemotingConfiguration.RegisterActivatedServiceType(serverType);   
1152For client-side type activation mode and location registration, you have the following:   
1153Type serverType = typeof(MyServer<int>);   
1154string url = ...; //some url initialization   
1155RemotingConfiguration.RegisterWellKnownClientType(serverType,url);   
1156When instantiating the remote server, simply provide the specific types, just as if you where using a local   
1157generic type:   
1158MyServer<int> obj;   
1159obj = new MyServer<int>();   
1160//Use obj   
1161Instead of using new, the client can optionally use the methods of the Activator class to connect to   
1162remote objects. When using Activator.GetObject() you need to provide the specific types to use, and   
1163provide the specific types when explicitly casting the returned Object:   
1164string url = ...; //some url initialization   
1165Type serverType = typeof(MyServer<int>);   
1166MyServer<int> obj;   
1167obj = (MyServer<int>)Activator.GetObject(serverType,url);   
1168//Use obj   
1169The Activator.CreateInstance() method has numerous overloaded versions. Some of the versions can   
1170be used with generic types:   
1171Type serverType = typeof(MyServer<int>);   
1172MyServer<int> obj;   
1173obj = (MyServer<int>)Activator.CreateInstance(serverType);   
1174//Use obj   
1175However, for generic types you cannot use the versions of CreateInstance() and   
1176CreateInstanceFrom() that accept the type in string format and return ObjectHandle, such as:   
1177public static ObjectHandle CreateInstance(string assemblyName,string   
1178typeName);   
1179Activator was designed without generics in mind, and it is therefore not completely type safe because   
1180of the explicit cast from Object. It is possible to compensate by defining the class GenericActivator, as   
1181shown in Code block 15.   
1182Code block 15. The GenericActivator   
1183public class GenericActivator   
1184{   
1185public static T CreateInstance<t>()   
1186{   
1187return (T)Activator.CreateInstance(typeof(T));   
1188}   
1189public static T GetObject<t>(string url)   
1190{   
1191return (T)Activator.GetObject(typeof(T),url);   
1192}   
1193}   
1194GenericActivator uses generic static methods to expose the most useful methods of Activator. You   
1195can use GenericActivator instead of Activator to activate generic and non-generic types. For example,   
1196using GetObject():   
1197public class MyServer : MarshalByRefObject   
1198{...}   
1199public class MyGenericServer<t> : MarshalByRefObject   
1200{...}   
1201string url = ...;//some url initialization   
1202MyServer obj1 = GenericActivator.GetObject<myserver>(url);   
1203MyGenericServer obj2 = GenericActivator.GetObject<myserver<int>&gt;(url);   
1204Or using CreateInstance():   
1205MyServer obj = GenericActivator.CreateInstance<myserver>();   
1206What Generics Cannot Do   
1207Under .NET 2.0, you cannot define generic Web services. That is, Web methods that use generic type   
1208parameters. The reason is that none of the Web service standards support generic services.   
1209You also cannot use generic types on a serviced component. The reason is that generics do not meet COM   
1210visibility requirements, which are required for serviced components (just like you could not use C++   
1211templates in COM or COM+).   
1212Conclusion   
1213C# generics are an invaluable addition to your development arsenal. They improve performance, type   
1214safety and quality, reduce repetitive programming tasks, simplify the overall programming model, and   
1215do so with elegant, readable syntax. While the roots of C# generics are C++ templates, C# takes   
1216generics to a new level by providing compile-time safety and support. C# utilizes two-phase compilation,   
1217metadata, and innovative concepts such as constraints and generic methods. No doubt future versions of   
1218C# will continue to evolve generics, adding new capabilities and extending generics to other areas   
1219of .NET Framework such as data access or localization.   
1220Juval Lowy is a software architect and the principal of IDesign, a consulting and training company. Juval   
1221is a Microsoft Regional Director for the Silicon Valley, wor</myserver></myserver<int></myserver></t></t></t></int></int></int></int></int></int></int></int></int></int></t></string></t></t></t></t></t></int></int></int></int></t></string></int></t></k></keyvaluepair<k,t></k,t></k,v></keyvaluepair<k,t></keyvaluepair<k,t></k,t></t></t></k,v></t></k,v></k,v></k,v></t></t></t></t></t></t></t></t></t></t></t></k,t></t></t></t></t></k,t></t></t></k,t></t></t></k,t></k,t></t></t></int,string></int,string></t></t></int,string></int,string></int,string></int,string></t></int,string></int,string></int,string></eventargs></eventargs></argstype></mypublisher,eventargs></sendertype,argstype></t></t></int></int></int></t></x></t></int></int></int></int></int></int></int></t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></int,string></int,string></int,string></int,string></int,string></t></t></int></string></int></x></t></int></t></t></t></t></t></t></t></t></t></t></t></int></t></x></t></int></t></int></t></t></t></t></t></t></t></t></int></t></t></t></t></t></t></t></t></t></int></t></int,string></int,string></int,string></t></t></t></t></k></k,t></k,t></k,t></k,t></t></t></t></t></t></k,t></int></k,t></t></k,t,u></k></k,t></k,t></k></k,t></k></k,t></keytype,datatype></keytype></keytype,datatype></k></k,t></t></t></t></k></t></k,t></t></t></k></k,t></t></t></t></k,t></k,t></k,t></int,string></int,string></k,t></int,string></datetime,string></datetime,string></int,string></int,string></keytype,datatype></keytype,datatype></u,v></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></k,t></double></int></t></t></t></int></int></t></int></int></t>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus