Export (0) Print
Expand All
Abortable Thread Pool
The Analytic Hierarchy Process
API Test Automation in .NET
Asynchronous HttpWebRequests, Interface Implementation, and More
Bad Code? FxCop to the Rescue
Basics of .NET Internationalization
Behind the Scenes: Discover the Design Patterns You're Already Using in the .NET Framework
BigInteger, GetFiles, and More
Binary Serialization of DataSets
Building Voice User Interfaces
Can't Commit?: Volatile Resource Managers in .NET Bring Transactions to the Common Type
CLR Inside Out: Base Class Library Performance Tips and Tricks
CLR Inside Out: Ensuring .NET Framework 2.0 Compatibility
CLR Inside Out: Extending System.Diagnostics
CLR Profiler: No Code Can Hide from the Profiling API in the .NET Framework 2.0
Concurrent Affairs: Build a Richer Thread Synchronization Lock
Custom Cultures: Extend Your Code's Global Reach With New Features In The .NET Framework 2.0
Cutting Edge: Collections and Data Binding
Const in C#, Exception Filters, IWin32Window, and More
Creating a Custom Metrics Tool
DataGridView
DataSets vs. Collections
Determining .NET Assembly and Method References
Experimenting with F#
File Copy Progress, Custom Thread Pools
Finalizers, Assembly Names, MethodInfo, and More
Got Directory Services?: New Ways to Manage Active Directory using the .NET Framework 2.0
High Availability: Keep Your Code Running with the Reliability Features of the .NET Framework
How Microsoft Uses Reflection
ICustomTypeDescriptor, Part 2
ICustomTypeDescriptor, Part 1
Iterating NTFS Streams
JIT and Run: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
Lightweight UI Test Automation with .NET
Low-Level UI Test Automation
Make Your Apps Fly with the New Enterprise Performance Tool
Managed Spy: Deliver The Power Of Spy++ To Windows Forms With Our New Tool
Memory Models: Understand the Impact of Low-Lock Techniques in Multithreaded Apps
Microsoft Java Virtual Machine Update
Microsoft .NET Framework Delivers the Platform for an Integrated, Service-Oriented Web, Part 2
Mini Dump Snapshots and the New SOS
Mutant Power: Create A Simple Mutation Testing System With The .NET Framework
NamedGZipStream, Covariance and Contravariance
.NET Internationalization Utilities
.NET Profiling: Write Profilers With Ease Using High-Level Wrapper Classes
No More Hangs: Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps
The Perfect Host: Create and Host Custom Designers with the .NET Framework 2.0
Phoenix Rising
Scheme Is Love
Security Enhancements in the .NET Framework 2.0
Sepia Tone, StringLogicalComparer, and More
Software Testing Paradoxes
Stay Alert: Use Managed Code To Generate A Secure Audit Trail
Stream Decorator, Single-Instance Apps
StringStream, Methods with Timeouts
SUPERASSERT Goes .NET
Tailor Your Application by Building a Custom Forms Designer with .NET
Test Harness Design Patterns
ThreadPoolPriority, and MethodImplAttribute
ThreadPoolWait and HandleLeakTracker
Three Vital FXCop Rules
A Tidal Wave of Change
To Confirm is Useless, to Undo Divine
Touch All the Bases: Give Your .NET App Brains and Brawn with the Intelligence of Neural Networks
Transactions for Memory
Trustworthy Software
Tune in to Channel 9
UDP Delivers: Take Total Control Of Your Networking With .NET and UDP
UI on the Fly: Use the .NET Framework to Generate and Execute Custom Controls at Run Time
Unexpected Errors in Managed Applications
Unhandled Exceptions and Tracing in the .NET Framework 2.0
Using Combinations to Improve Your Software Test Case Generation
Wandering Code: Write Mobile Agents In .NET To Roam And Interact On Your Network
What Makes Good Code Good?
XML Comments, Late-bound COM, and More
Expand Minimize

Generics FAQ: Fundamentals

 

Juval Lowy

October 2005

Applies to:
   Generic Types

Summary: Review frequently asked questions regarding generic types and their various uses. (51 printed pages)

Contents

What Is a Generic Type?
What Is a Generic Type Parameter?
What Is a Generic Type Argument?
What Is a Constructed Type?
What Is an Open Constructed Type?
What Is a Closed Constructed Type?
How Do I Use a Generic Type?
How Do I Initialize a Generic Type Parameter?
What Are the Benefits of Generics?
Why Can't I Use Type-Specific Data Structures Instead of Generics?
When Should I Use Generics?
Are Generics Covariant, Contra-Variant, or Invariant?
What Can Define Generic Type Parameters? What Types Can Be Generic?
Can Methods Define Generic Type Parameters? How Do I Call Such Methods?
Can I Derive From a Generic Type Parameter?
What Is a Generic Type Inference?
What Are Constraints?
What Can I Not Use Constraints With?
Why Can I Not Use Enums, Structs, or Sealed Classes as Generic Constraints?
Is Code that Uses Generics Faster than Code that Does Not?
Is an Application That Uses Generics Faster than an Application That Does Not?
How Are Generics Similar to Classic Visual C++ Templates?
How Are Generics Different from Classic Visual C++ Templates?
What Is the Difference Between Using Generics and Using Interfaces (or Abstract Classes)?
How Are Generics Implemented?
Why Can't I Use Operators on Naked Generic Type Parameters?
When Can I Use Operators on Generic Type Parameters?
Can I Use Generic Attributes?
Are Generics CLS Compliant?

What Is a Generic Type?

A generic type is a type that uses generic type parameters. For example, the type LinkedList<K,T>, defined as:

[C#]

public class LinkedList<K,T>
{...}

[Visual Basic]

Public Class LinkedList(Of K, T)
    ...
End Class

[C++]

generic <typename K, typename T>
public ref class LinkedList
{...};

is a generic type, because it uses the generic type parameters K and T, where K is the list's key and T is the type of the data item stored in the list. What is special about generic types is that you code them once, yet you can use them with different parameters. Doing so has significant benefits—you reuse your development and testing efforts, without compromising type safety and performance, and without bloating your code.

What Is a Generic Type Parameter?

A generic type parameter is the place holder a generic type uses. For example, the generic type LinkedList<K,T>, defined as:

[C#]

public class LinkedList<K,T>
{...}

[Visual Basic]

Public Class LinkedList(Of K, T)
    ...
End Class

[C++]

generic <typename K, typename T>
public ref class LinkedList
{...};

uses two type parameters - K and T, where K is the list's key and T is the type of the data item stored in the list. Using generic type parameters allows the linked list to defer the decision on the actual types to use. In fact, it is up to the client of the generic linked list to specify the generic type parameters to use.

What Is a Generic Type Argument?

A generic type argument is the type the client specifies to use instead of the type parameter. For example, given this generic type definition and declaration:

[C#]

public class MyClass<T>
{...}
MyClass<int> obj = new  MyClass<int>();

[Visual Basic]

Public Class SomeClass(Of T)
    ...
End Class
Dim obj As New SomeClass(Of Integer)

[C++]

generic <typename T>
public ref class MyClass
{...};
MyClass<int> ^obj = gcnew  MyClass<int>;

T is the type parameter, while integer is the type argument.

What Is a Constructed Type?

A constructed type is any generic type that has at least one type argument.

For example, given this generic linked list definition:

[C#]

public class LinkedList<T>
{...}

[Visual Basic]

Public Class LinkedList(Of T)
    ...
End Class

[C++]

generic <typename T>
public ref class LinkedList
{...};

Then the following is a constructed generic type:

[C#]

LinkedList<string>

[Visual Basic]

LinkedList(Of String)

[C++]

LinkedList<String ^>

To qualify as a constructed type you can also specify type parameters to the generic type:

[C#]

public class MyClass<T>
{
   LinkedList<T> m_List; //Constructed type 
}

[Visual Basic]

Public Class SomeClass(Of T)
    Dim m_List As LinkedList(Of T) ' Constructed type
End Class

[C++]

generic <typename T>
public ref class MyClass
{
   LinkedList<T> ^m_List; //Constructed type 
};

What Is an Open Constructed Type?

A open constructed type is any generic type that which contains at least one type parameter used as a type argument. For example, given this definition:

[C#]

public class LinkedList<K,T>
{...}

[Visual Basic]

Public Class LinkedList(Of K, T)
    ...
End Class

[C++]

generic <typename K, typename T>
public ref class LinkedList
{...};

Then the following declarations of LinkedList<K,T> member variables are all open constructed types:

[C#]

public class MyClass<K,T>
{
   LinkedList<K,T>      m_List1; //Open constructed type 
   LinkedList<K,string> m_List2; //Open constructed type 
   LinkedList<int,T>    m_List3; //Open constructed type 
}

[Visual Basic]

Public Class SomeClass(Of K, T)
    Dim m_List1 As LinkedList(Of K, T)      'Open constructed type
    Dim m_List2 As LinkedList(Of K, String) 'Open constructed type
    Dim m_List3 As LinkedList(Of Integer, T)'Open constructed type
End Class

[C++]

generic <typename K, typename T>
public ref class MyClass
{
   LinkedList<K, T>      ^m_List1; //Open constructed type 
   LinkedList<K, String ^> ^m_List2; //Open constructed type 
   LinkedList<int, T>    ^m_List3; //Open constructed type 
};

What Is a Closed Constructed Type?

A closed constructed type is a generic type that which contains no type parameters as type arguments. For example, given this definition:

[C#]

public class LinkedList<K,T>
{...}

[Visual Basic]

Public Class LinkedList(Of K, T)
    ...
End Class

[C++]

generic <typename K, typename T>
public ref class LinkedList
{...};

Then the following declarations of LinkedList<K,T> member variables are all closed constructed types:

[C#]

LinkedList<int,string> list1; //Closed constructed type 
LinkedList<int,int>    list2; //Closed constructed type 

[Visual Basic]

Dim list1 As LinkedList(Of Integer, String) 'Closed constructed type
Dim list2 As LinkedList(Of Integer, Integer)'Closed constructed type

[C++]

LinkedList<int, String ^> ^list1; //Closed constructed type 
LinkedList<int, int>      ^list2; //Closed constructed type 

How Do I Use a Generic Type?

or

How Do I Initialize a Generic Type Parameter?

When declaring a generic type, you need to specify the types that will replace the type parameters in the declaration. These are known as type arguments to the generic type. Type arguments are simply types. For example, when using this generic linked list:

[C#]

public class LinkedList<K,T>
{
   public void AddHead(K key,T item);
   //Rest of the implementation 
}

[Visual Basic]

Public Class LinkedList(Of K, T)
    Public Sub AddHead(ByVal key As K, ByVal item As T)
    'Rest of the implementation
End Class

[C++]

generic <typename K, typename T>
public ref class LinkedList
{
   public: void AddHead(K key,T item);
   //Rest of the implementation 
};

You need to specify which types to use for K, the list's key, and T, the data items stored in the list. You specify the types in two places: when declaring the list's variable and when instantiating it:

[C#]

LinkedList<int,string> list = new LinkedList<int,string>();
list.AddHead(123,"ABC");

[Visual Basic]

Dim list As New LinkedList(Of Integer, String)
list.AddHead(123, "ABC")

[C++]

LinkedList<int, String ^> ^list = gcnew LinkedList<int, String ^>;
list->AddHead(123,"ABC");

Once you specify the types to use, you can simply call methods on the generic type, providing appropriate values of the previously specified types.

A generic type that has type arguments already, such as LinkedList<int,string> is called a constructed type.

When specifying type arguments for generic types, you can actually provide type parameters. For example, consider this definition of the Node<K,T> class, which is used as a node in a linked list:

[C#]

class Node<K,T>
{
   public K Key;
   public T Item;
   public Node<K,T> NextNode;

   public Node(K key,T item,Node<K,T> nextNode)
   {
      Key      = key;
      Item     = item;
      NextNode = nextNode;
   }
}

[Visual Basic]

Class Node(Of K, T)
    Public Key As K
    Public Item As T
    Public NextNode As Node(Of K, T)
    Public Sub Node(ByVal key As K, ByVal item As T, ByVal nextNode As Node(Of K, T))
        Me.Key = key
        Me.Item = item
        Me.NextNode = nextNode
    End Sub
End Class

[C++]

generic <typename K, typename T>
ref class Node
{
public: 
   K Key;
   T Item;
   Node<K,T> ^NextNode;

   Node(K key,T item,Node<K,T> ^nextNode)
   {
      Key      = key;
      Item     = item;
      NextNode = nextNode;
   }
};

The Node<K,T> class contains as a member variable a reference to the next node. That member must be provided with the type to use instead of its generic type parameters. The node specifies its own type parameters in this case.

Another example of specifying generic type parameters to a generic type is how the linked list itself may declare and use the node:

[C#]

public class LinkedList<K,T>
{
   Node<K,T> m_Head;   

   public void AddHead(K key,T item)
   {...}
}

[Visual Basic]

Public Class LinkedList(Of K, T)
    Dim m_Head As Node(Of K, T)
    Public Sub AddHead(ByVal key As K, ByVal item As T)
        ...
    End Sub
End Class

[C++]

generic <typename K, typename T>
public ref class LinkedList
{
   Node<K,T> ^m_Head;   

   public: void AddHead(K key,T item)
   {...}
};

Note that the use of K and T in the linked list as the names of the type arguments is purely for readability purposes, to make the use of the node more consistent. You could have defined the linked list with any other generic type parameter names, in which case, you need to pass them along to the node as well:

[C#]

public class LinkedList<Key,Item>
{
   Node<Key,Item> m_Head;   

   public void AddHead(Key key,Item item)
   {...}
}

[Visual Basic]

Public Class LinkedList(Of Key, Item)
    Dim m_Head As Node(Of Key, Item)
    Public Sub AddHead(ByVal key As Key, ByVal item As Item)
        ...
    End Sub
End Class

[C++]

generic <typename Key, typename Item>
public ref class LinkedList
{
   Node<Key,Item> ^m_Head;   
   public: void AddHead(Key key,Item item) {...}
};

What Are the Benefits of Generics?

Without generics, if you would like to develop general-purpose data structures, collections or utility classes, you would have to base all those on object. For example, here is the object-based IList interface, found in the System.Collections namespace:

[C#]

public interface IList : ICollection
{
   int Add(object value);
   bool Contains(object value);
   int IndexOf(object value);
   void Insert(int index, object value);
   void Remove(object value);
   object this[int index]{ get; set; }
   //Additional members 
}

[Visual Basic]

Public Interface IList
    Inherits ICollection

    Function add(ByVal value As Object) As Integer
    Function contains(ByVal value As Object) As Boolean
    Sub insert(ByVal index As Integer, ByVal value As Object)
    Sub Remove(ByVal value As Object)
    Property Item(ByVal index As Integer) As Object
    'Additional members
End Interface

[C++]

public interface class IList : ICollection, IEnumerable
{
   int Add(Object ^value);
   bool Contains(Object ^value);
   void Insert(int index, Object ^value);
   void Remove(Object ^value);
   property Object ^ default[]
   { 
      Object ^ get(int index); 
      void set(int index, Object ^value); 
   }
   //Additional members 
};

Clients of this interface can use it to manipulate linked lists of any type, including value types such as integers or reference types such as strings:

[C#]

IList numbers = new ArrayList();
numbers.Add(1); //Boxing
int number = (int)numbers[0];//Unboxing

IList names = new ArrayList();
names.Add("Bill");
string name = (string)names[0];//Casting

[Visual Basic]

Dim numbers As IList = New ArrayList()
numbers.Add(1) 'Boxing
Dim number As Integer = CType(numbers(0), Integer) 'Unboxing
Dim names As IList = New ArrayList()
names.Add("Bill")
Dim name As String = CType(names(0), String) 'Casting

[C++]

IList ^numbers = gcnew ArrayList;
numbers->Add(1); //Boxing
int number = (int)numbers[0]; //Unboxing

IList ^names = gcnew ArrayList;
names->Add("Bill");
String ^name = (String ^)names[0]; //Casting

However, because IList is object-based, every use of a value type would force boxing it in an object, and unboxing it when using the indexer. Use of reference types forces the use of a cast which both complicates the code and has an impact on performance.

Now, consider the generics-equivalent interface, IList<T>, found in the System.Collections.Generic namespace:
[C#]

public interface IList<T> : ICollection<T>
{
   int IndexOf(T item);
   void Insert(int index, T item);
   void RemoveAt(int index);
   T this[int index]{ get; set; }
}

[Visual Basic]

<DefaultMember("Item")> _
Public Interface IList(Of T)
    Inherits ICollection(Of T)

    Function IndexOf(ByVal item As T) As Integer
    Sub Insert(ByVal index As Integer, ByVal item As T)
    Sub RemoveAt(ByVal index As Integer)
    Property Item(ByVal index As Integer) As T
End Interface

[C++]

generic <typename T>
public interface class IList : ICollection<T>
{
   int IndexOf(T item);
   void Insert(int index, T item);
   void RemoveAt(int index);
   property T default[]   
   { 
      T get(int index); 
     void set(int index, T value); 
   }
   //Additional members 
};

Clients of this IList<T> can also use it to manipulate linked lists of any type, but doing so without any performance penalties. When using a value type instead of the type parameters, no boxing or unboxing is performed, and when using a reference type, no cast is required:

[C#]

IList<int> numbers = new List<int>();
numbers.Add(1);
int number = numbers[0];

IList<string> names = new List<string>();
names.Add("Bill");
string name = names[0];

[Visual Basic]

Dim numbers As IList(Of Integer) = New List(Of Integer)()
numbers.Add(1)
Dim number As Integer = numbers(0)

Dim names As IList(Of String) = New List(Of String)()
names.Add("Bill")
Dim name As String = names(0)

[C++]

IList<int> ^numbers = gcnew List<int>;
numbers->Add(1);
int number = numbers[0];

IList<String ^> ^names = gcnew List<String ^>;
names->Add("Bill");
String ^name = names[0];

Various benchmarks have shown that in intense calling patterns, generics yield on average 200 percent performance improvement when using value types, and some 100 percent performance improvement when using reference types.

However, performance is not the main benefit of generics. In most real-life applications, bottle necks such as I/O will mask out any performance benefit from generics. The most significant benefit of generics is type-safety. With the object-based solutions, mismatch in type will still get complied, but yield an error at runtime:

[C#]

IList numbers = new ArrayList();
numbers.Add(1);
string name = (string)numbers[0]; //Run-time error

[Visual Basic]

Public Class SomeClass
   ...
End Class

Dim numbers As IList = New ArrayList()
numbers.Add(1)
Dim obj As SomeClass = CType(numbers(0), SomeClass) 'Run-time error

[C++]

IList ^numbers = gcnew ArrayList;
numbers->Add(1);
String ^name = (String ^)numbers[0]; //Run-time error

In large code bases, such errors are notoriously difficult to track down and resolve. With generics, such code would never get compiled:

[C#]

IList<int> numbers = new List<int>();
numbers.Add(1);
string name = numbers[0]; //Compile-time error

[Visual Basic]

Public Class SomeClass
   ...
End Class

Dim numbers As IList(Of Integer) = New List(Of Integer)()
numbers.Add(1)
Dim obj As SomeClass = numbers(0)'Compile-time error

[C++]

IList<int> ^numbers = gcnew List<int>;
numbers->Add(1);
String ^name = numbers[0]; //Compile-time error

Why Can't I Use Type-Specific Data Structures Instead of Generics?

To avoid the type-safety problem without generics, you might be tempted to use type-specific interfaces and data structure, for example:

[C#]

public interface IIntegerList 
{
   int Add(int value);
   bool Contains(int value);
   int IndexOf(int value);
   void Insert(int index, int value);
   void Remove(int value);
   int this[int index]{ get; set; }
   //Additional members 
}

[Visual Basic]

Public Interface IIntegerList
    Function Add(ByVal value As Integer) As Integer
    Function Contains(ByVal value As Integer) As Boolean
    Function IndexOf(ByVal value As Integer) As Integer
    Sub Insert(ByVal index As Integer, ByVal value As Integer)
    Sub Remove(ByVal value As Integer)
    Property Item(ByVal index As Integer) As Integer
    'Additional members
End Interface

[C++]

public interface class IIntegerList 
{
   int Add(int value);
   bool Contains(int value);
   int IndexOf(int value);
   void Insert(int index, int value);
   property int default[]
   { 
      int get(int index); 
      void set(int index, int value); 
   }
   //Additional members 
};

The problem with that approach is that you will need a type-specific interface and implementation per data type you need to interact with, such as a string or a Customer. If you have a defect in your handling of the data items, you will need to fix it in as many places as types, and that is simply error-prone and impractical. With generics, you get to define and implement your logic once, yet use it with any type you want.

When Should I Use Generics?

You should use generics whenever you have the option to. Meaning, if a data structure or a utility class offers a generic version, you should use the generic version, not the object-based methods. The reason is that generics offer significant benefits, including productivity, type safety and performance, at literally no cost to you. Typically, collections and data structures such as linked lists, queues, binary trees etc will offer generics support, but generics are not limited to data structures. Often, utility classes such as class factories or formatters also take advantage of generics. The one case where you should not take advantage of generics is cross-targeting. If you develop your code to target .NET 1.1 or earlier, then you should not use any of the new .NET 2.0 features, including generics. In C# 2.0, you can even instruct the compiler in the project settings (under Build | Advanced) to use only C# 1.0 syntax (ISO-1).

Are Generics Covariant, Contra-Variant, or Invariant?

Generic types are not covariant. Meaning, you cannot substitute a generic type with a specific type argument, with another generic type that uses a type argument that is the base type for the first type argument. For example, the following statement does not compile:

[C#]

class MyBaseClass
{}
class MySubClass : MyBaseClass
{}
class MyClass<T>
{}
//Will not compile 
MyClass<MyBaseClass> obj = new MyClass<MySubClass>();

[Visual Basic]

Public Class MyBaseClass
    ...
End Class
Public Class MySubClass
    Inherits MyBaseClass
    ...
End Class
Public Class SomeClass(Of T)
    ...
End Class
'Will not compile.
Dim obj As SomeClass(Of MyBaseClass) = New SomeClass(Of MySubClass)()

[C++]

ref class MyBaseClass
{};
ref class MySubClass : MyBaseClass
{};
generic <typename T> where T : MyBaseClass
ref class MyClass
{};
//Will not compile 
MyClass<MySubClass ^> ^obj = gcnew MyClass<MyBaseClass ^>;

[C#]

Using the same definition as in the example above, it is also true that MyClass<MyBaseClass> is not the base type of MyClass<MySubClass>:

Debug.Assert(typeof(MyClass<MyBaseClass>) != typeof(MyClass<MySubClass>).BaseType);

[Visual Basic]

Using the same definition as in the example above, it is also true that SomeClass(Of MyBaseClass) is not the base type of SomeClass(Of MySubClass):

Debug.Assert(GetType(SomeClass(Of MyBaseClass)) IsNot GetType(SomeClass(Of MySubClass)).BaseType)

[C++]

Using the same definition as in the example above, it is also true that MyClass<MyBaseClass> is not the base type of MyClass<MySubClass>:

Type ^baseType = typeid<MyClass<MyBaseClass ^> ^>;
Type ^subType  = typeid<MyClass<MySubClass  ^> ^>;
Debug::Assert(baseType != subType);

This would not be the case if the generic types were contra-variant.

Because generics are not covariant, when overriding a virtual method that returns a generic type parameter, you cannot provide a subtype of that type parameter as the definition of the overriding method:

For example, the following statement does not compile:

[C#]

class MyBaseClass<T>
{
   public virtual T MyMethod()
   {...}
}
class MySubClass<T,U> : MyBaseClass<T> where T : U
{
   //Invalid definition: 
   public override U MyMethod()
   {...}
}

[Visual Basic]

Class MyBaseClass(Of T)
   Public Overridable Function MyMethod() As T
   ...
   End Function
End Class

Class MySubClass(Of T As U, U) 
   Inherits MyBaseClass(Of T)
End Class

[C++]

C++ doesn't allow a generic type to be used as a constraint.

That said, constraints are covariant. For example, you can satisfy a constraint using a sub type of the constraint's type:

[C#]

class MyBaseClass
{}
class MySubClass : MyBaseClass
{}
class MyClass<T> where T : MyBaseClass
{}

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

[Visual Basic]

Class MyBaseClass
    ...
End Class
Class MySubClass
    Inherits MyBaseClass
    ...
End Class
Class SomeClass(Of T As MyBaseClass)
    ...
End Class
Dim obj As New SomeClass(Of MySubClass)()

[C++]

ref class MyBaseClass
{};
ref class MySubClass : MyBaseClass
{};
generic <typename T> where T : MyBaseClass
ref class MyClass 
{};

MyClass<MySubClass ^> ^obj = gcnew MyClass<MySubClass ^>;

You can even further restrict constraints this way:

[C#]

class BaseClass<T>  where T : IMyInterface
{}
interface IMyOtherInterface : IMyInterface
{}
 
class SubClass<T> : BaseClass<T> where T : IMyOtherInterface 
{}

[Visual Basic]

Class BaseClass(Of T As IMyInterface)
    ...
End Class
Interface IMyOtherInterface
    Inherits IMyInterface
    ...
End Interface
Class SubClass(Of T As IMyOtherInterface)
    Inherits BaseClass(Of T)
    ...
End Class

[C++]

interface class IMyInterface 
{};
generic <typename T> where T : IMyInterface
ref class BaseClass  
{};
interface class IMyOtherInterface : IMyInterface
{};
generic <typename T> where T : IMyOtherInterface
ref class SubClass : BaseClass<T>
{};

Finally, generics are invariant, because there is no relationship between two generic types with different type arguments, even if those type arguments do have an is-as relationship, for example, List<int> has nothing to do with List<object>, even though an int is an object.

What Can Define Generic Type Parameters? What Types Can Be Generic?

Classes, interfaces, structures and delegates, can all be generic types. Here are a few examples from the .NET Framework:

[C#]

public interface IEnumerator<T> : IEnumerator,IDisposable
{
   T Current{get;}
}

public class List<T> : IList<T> //More interfaces 
{
   public void Add(T item);
   public bool Remove(T item);
   public T this[int index]{get;set;}
   //More members 
}

public struct KeyValuePair<K,V>
{
   public KeyValuePair(K key,V value);   
   public K Key;
   public V Value;
}

public delegate void EventHandler<E>(object sender,E e) where E : EventArgs;

[Visual Basic]

Public Interface IEnumerator(Of T)
    Inherits IDisposable , IEnumerator
    ReadOnly Property current As T
End Interface

Public Class list(Of T)
    Inherits IList(Of T)'More interfaces

    Public Sub Add(ByVal item As T)
    Public Function Remove(ByVal item As T) As Boolean
    ' More members
End Class

Public Struct KeyValuePair(Of K, V)
    Public Sub New(key As K, value As V)
    Public Key As K
    Public Value As V
End Structure
Public Delegate Sub EventHandler(Of E As EventArgs) _
  (ByVal sender As Object, ByVal e As E)

[C++]

generic <typename T>
public interface class IEnumerator : IEnumerator,IDisposable
{
   property T Current { T get(); }
};

generic <typename T>
public ref class List : IList<T> //More interfaces 
{
public: 
   void Add(T item);
   bool Remove(T item);
   property T default[] { T get(int index); void set(int index, T value); }
   //More memebers 
};
generic <typename K, typename V>
public ref struct KeyValuePair
{
public: 
   KeyValuePair(K key, V value);   
   K Key;
   V Value;
};

generic <typename T> where T: EventArgs
public delegate void EventHandler(Object ^sender, T e);

In addition, both static and instance methods can rely on generic type parameters, independent of the types that contain them:

[C#]

public sealed class Activator : _Activator
{
   public static T CreateInstance<T>();
   //Additional memebrs 
}

[Visual Basic]

Public NotInheritable Class Activator
      Implements _Activator

    Public Shared Function CreateInstance(Of T)() As T
    ' Additional members.
End Class

[C++]

public ref class Activator sealed : _Activator
{
   public: generic <typename T>
           static T CreateInstance();
   //Additional members 
};

Enumerations on the other hand cannot define type parameters, and the same goes for attributes.

Can Methods Define Generic Type Parameters? How Do I Call Such Methods?

Yes. Both instance and static methods can define generic type parameters, and do so independently of their containing class. For example:

[C#]

public class MyClass
{
   public void MyInstanceMethod<T>(T t)
   {...}
   public static void MyStaticMethod<T>(T t)
   {...}
}

[Visual Basic]

Public Class SomeClass
    Public Sub MyInstanceMethod(Of T)(ByVal value As T)
        ...
    End Sub
    Public Shared Sub MySharedMethod(Of T)(ByVal value As T)
        ...
    End Sub
End Class

[C++]

public ref class MyClass
{
public:      
   generic <typename T>
   void MyInstanceMethod (T t)
   {...}
   generic <typename T>
   static void MyStaticMethod (T t)
   {...}
};

The benefit of a method that defines generic type parameters is that you can call the method passing each time different parameter types, without ever overloading the method. When you call a method that defines generic type parameters, you need to provide the type arguments at the call site:

[C#]

MyClass obj = new MyClass();
obj.MyInstanceMethod<int>(3);
obj.MyInstanceMethod<string>("Hello");

MyClass.MyStaticMethod<int>(3);
MyClass.MyStaticMethod<string>("Hello");

[Visual Basic]

Dim obj As New SomeClass()
obj.MyInstanceMethod(Of Integer)(3)
obj.MyInstanceMethod(Of String)("Hello")
SomeClass.MySharedMethod(Of Integer)(3)
SomeClass.MySharedMethod(Of String)("Hello")

[C++]

MyClass ^obj = gcnew MyClass;
obj->MyInstanceMethod<int>(3);
obj->MyInstanceMethod<String ^>("Hello");

MyClass::MyStaticMethod<int>(3);
MyClass::MyStaticMethod<String ^>("Hello");

If type-inference is available, you can omit specifying the type arguments at the call site:

[C#]

MyClass obj = new MyClass();
obj.MyInstanceMethod(3);
obj.MyInstanceMethod("Hello");

MyClass.MyStaticMethod(3);
MyClass.MyStaticMethod("Hello");

[Visual Basic]

Dim obj As New SomeClass()
obj.MyInstanceMethod(3)
obj.MyInstanceMethod("Hello")
SomeClass.MySharedMethod(3)
SomeClass.MySharedMethod("Hello")

[C++]

MyClass ^obj = gcnew MyClass;
obj->MyInstanceMethod(3);
obj->MyInstanceMethod(gcnew String("Hello"));

MyClass::MyStaticMethod(3);
MyClass::MyStaticMethod(gcnew String("Hello"));

Can I Derive From a Generic Type Parameter?

You cannot define a class that derives from its own generic type parameter:

[C#]

public class MyClass<T> : T //Does not compile 
{...}

[Visual Basic]

Public Class SomeClass(Of T)
    Inherits T ' Does not compile
    ...
End Class

[C++]

generic <typename T>
public ref class MyClass : T //Does not compile 
{...};

What Is a Generic Type Inference?

Generic type inference is the compiler's ability to infer which type arguments to use with a generic method, without the developer having to specify it explicitly. For example, consider the following definition of generic methods:

[C#]

public class MyClass
{
   public void MyInstanceMethod<T>(T t)
   {...}
   public static void MyStaticMethod<T>(T t)
   {...}
}

[Visual Basic]

Public Class SomeClass
    Public Sub MyInstanceMethod(Of T)(ByVal value As T)
        ...
    End Sub
    Public Shared Sub MySharedMethod(Of T)(ByVal value As T)
        ...
    End Sub
End Class

[C++]

public class MyClass
{
public: 
   generic <typename T>      
   void MyInstanceMethod(T t) {...}
   generic <typename T>   
   static void MyStaticMethod (T t) {...}
};

When invoking these methods, you can omit specifying the type arguments for both the instance and the static methods:

[C#]

MyClass obj = new MyClass();
obj.MyInstanceMethod(3); //Compiler infers T as int
obj.MyInstanceMethod("Hello");//Compiler infers T as string

MyClass.MyStaticMethod(3); //Compiler infers T as int
MyClass.MyStaticMethod("Hello");//Compiler infers T as string

[Visual Basic]

Dim obj As New SomeClass()
obj.MyInstanceMethod(3) ' Compiler infers T as int
obj.MyInstanceMethod("Hello") ' Compiler infers T as String
SomeClass.MySharedMethod(3) ' Compiler infers T as Integer
SomeClass.MySharedMethod("Hello") ' Compiler infers T as string

[C++]

MyClass ^obj = gcnew MyClass;
obj->MyInstanceMethod(3); //Compiler infers T as int
MyClass::MyStaticMethod(3); //Compiler infers T as int

Note that type inferring is possible only when the method takes an argument of the inferred type arguments. For example, in the CreateInstance<T>() method of the Activator class, defined as:

[C#]

public sealed class Activator : _Activator
{
   public static T CreateInstance<T>();
   //Additional memebrs 
}

[Visual Basic]

Public NotInheritable Class Activator
      Implements _Activator

    Public Shared Function CreateInstance(Of T)() As T
    ' Additional members.
End Class

[C++]

public ref class Activator sealed : _Activator
{
public: 
   generic <typename T>
   static T CreateInstance ();
   //Additional members 
};

type inference is not possible, and you need to specify the type arguments at the call site:

[C#]

class MyClass
{...} 
MyClass obj = Activator.CreateInstance<MyClass>(); 

[Visual Basic]

Public Class SomeClass
    ...
End Class
Dim obj As SomeClass = activator.createInstance(Of SomeClass)()

[C++]

ref class MyClass
{...}; 
MyClass ^obj = Activator::CreateInstance<MyClass ^>(); 

Note also that you cannot rely on type inference at the type level, only at the method level. In the following example, you must still provide the type argument T even though the method takes a T parameter:

[C#]

public class MyClass<T>
{
   public static void MyStaticMethod<U>(T t,U u)
   {...}
}
MyClass<int>.MyStaticMethod(3,"Hello");//No type inference for the integer 

[Visual Basic]

Public Class SomeClass(Of T)
    Public Shared sub MySharedMethod(Of U)(ByVal item As T, ByVal uu As U)
        ...
    End Sub
End Class
SomeClass(Of Integer).MySharedMethod(3, "Hello") 
'No type inference for the integer

[C++]

generic <typename T>
public ref class MyClass
{
   public: generic <typename U> static void MyStaticMethod (T t,U u)
   {...}
};
MyClass<int>::MyStaticMethod(3, 22.7);//No type inference for the integer 

What Are Constraints?

Constraints allow additional contextual information to be added to the type parameters of generic types. The constraints limit the range of types that are allowed to be used as type arguments, but at the same time, they add information about those type parameters. Constraints ensure that the type arguments specified by the client code are compatible with the generic type parameters the generic type itself uses. Meaning, constraints prevent the client from specifying types as type arguments that do not offer the methods, properties, or members of the generic type parameters that the generic type relies upon.

After applying a constraint you get IntelliSense reflecting the constraints when using the generic type parameter, such as suggesting methods or members from the base type.

There are three types of constraints:

Derivation constraint indicates to the compiler that the generic type parameters derives from a base type such an interface or a particular base class. For example, in the following example, the linked list applies a constraint of deriving from IComparable<T> on its generic type parameter. This is required so that you could implement a search. sorting or indexing functionality on the list:

[C#]

class Node<K,T>
{
   public K Key;
   public T Item;
   public Node<K,T> NextNode;
} 

public class LinkedList<K,T> where K : IComparable<K>
{
   Node<K,T> m_Head;

   public T this[K key]
   {
      get
      {
         Node<K,T> current = m_Head;
         while(current.NextNode != null)
         {
            if(current.Key.CompareTo(key) == 0)
               break;
            else      
               current = current.NextNode;
         }
         return current.Item; 
      }
   }
   //Rest of the implementation 
}

[Visual Basic]

Class Node(Of K, T)
    Public Key As K
    Public Item As T
    Public NextNode As Node(Of K, T)
End Class

Public Class LinkedList(Of K As IComparable(Of K), T)
    Dim m_Head As Node(Of K, T)
    Public ReadOnly Property Item(ByVal key As K) As T
        Get
            Dim current As Node(Of K, T) = m_Head
            While current.NextNode IsNot Nothing
                If (current.Key.CompareTo(key) = 0) Then
                    Exit While
                Else
                    current = current.NextNode
                End If
            End While
            Return current.item
        End Get
    End Property
    ' Rest of the implementation
End Class

[C++]

generic <typename K, typename T>
ref class Node
{
public: K Key;
        T Item;
        Node<K,T> ^NextNode;
};
generic <typename K, typename T> where K : IComparable<K>
public ref class LinkedList
{
public:
   Node<K,T> ^m_Head;
public: 
   property T default[]
   {
      T get(K key)
      {
         Node<K,T> ^current = m_Head;
         while(current->NextNode)
         {
            if(current->Key->CompareTo(key)==0)
               break;
            else      
               current = current->NextNode;
         }
         return current->Item; 
      }
   }
   //Rest of the implementation 
};

You can provide constraints for every generic type parameter that your class declares, for example:

[C#]

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

[Visual Basic]

Public Class LinkedList(Of K As IComparable(Of K), T As ICloneable)
    ...
End Class

[C++]

generic <typename K, typename T> where K : IComparable<K>
                      where T : ICloneable
public ref class LinkedList  
{ ... };                         

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

[C#]

public class MyBaseClass
{...}
public class MyClass<T> where T : MyBaseClass
{...}

[Visual Basic]

Public Class MyBaseClass
    ...
End Class
Public Class SomeClass(Of T As MyBaseClass)
    ...
End Class

[C++]

public ref class MyBaseClass
{...};
generic <typename T> where T : MyBaseClass
public ref class MyClass
{...};

However, you can only use one base class at most in a constraint because neither C#, Visual Basic or managed C++ support multiple inheritance of implementation. Obviously, the base class you constrain to cannot be a sealed 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:

[C#]

public class LinkedList<K,T> where K : MyBaseKey,IComparable<K>
{...}

[Visual Basic]

Public Class LinkedList(Of K As {MyBaseKey, IComparable(Of K)}, T)
    ...
End Class

[C++]

generic <typename K, typename T> where K : MyBaseKey, IComparable<K>
public class LinkedList
{...};

The constructor constraint indicates to the compiler that the generic type parameter exposes a default public constructor (a public constructor with no parameters). For example:

[C#]

class Node<K,T> where K : new()
               where T : new()
{
   public K Key;
   public T Item;
   public Node<K,T> NextNode;

   public Node()
   {
      Key      = new K(); //Compiles because of the constraint
      Item     = new T(); //Compiles because of the constraint
      NextNode = null;
   }
   //Rest of the implementation    
}

[Visual Basic]

Class Node(Of K As New, T As New)
    Public Key  As K
    Public Item As T
    Public NextNode As Node(Of K, T)
    Public Sub New()
        Key  = New K()' Compiles because of the constraint
        Item = New T()' Compiles because of the constraint
        NextNode = Nothing
    End Sub
    ' Rest of the implementation.
End Class

[C++]

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

[C#]

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

[Visual Basic]

Public Class LinkedList(Of K As {IComparable(Of K), New}, T As New)
    ...
End Class

The reference and value type constraint is used to constrain the generic type parameter to be a value or a reference type. For example, you can constrain a generic type parameter to be a value type (such as an int, a bool, and enum, or any structure):

[C#]

public class MyClass<T> where T : struct 

{...}

[Visual Basic]

Public Class SomeClass(Of T As Structure)
    ...
End Class

Similarly, you can constrain a generic type parameter to be a reference type (a class):

[C#]

public class MyClass<T> where T : class 

{...}

[Visual Basic]

Public Class SomeClass(Of T As Class)
    ...
End Class

The reference and value type constraint cThe value/reference type constraint cannot be used with a base class constraint, but it can be combined with any other constraint. When used, the value/reference type constraint must appear first in the constraint list.

It is important to note that although constraints are optional, they are often essential when developing a generic type. Without constraints, the compiler follows the more conservative, type-safe approach and only allows access to object-level functionality in your generic type parameters. Constraints are part of the generic type metadata so that the client-side compiler can take advantage of them as well. The client-side compiler only allows the client developer to use types that comply with the constraints, thus enforcing type safety.

What Can I Not Use Constraints With?

You can only place a derivation constraint on a type parameter (be it an interface derivation or a single base class derivation). In C# and Visual Basic, you can also use a default constructor constraint and a value or reference type constraint. While everything else is implicitly not allowed, it is worth mentioning the specific cases that are not possible:

  • You cannot constrain a generic type to have any specific parameterized construct.
  • You cannot constrain a generic type to derive from a sealed class.
  • You cannot constrain a generic type to derive from a static class.
  • You cannot constrain a public generic type to derive from another internal type.
  • You cannot constrain a generic type to have a specific method, be it a static or an instance method.
  • You cannot constrain a generic type to have a specific public event.
  • You cannot constrain a generic type parameter to derive from System.Delegate or System.Array.
  • You cannot constrain a generic type parameterfundamentals_topic2 to be serializable.
  • You cannot constrain a generic type parameterfundamentals_topic2 to be COM-visible.
  • You cannot constrain a generic type parameter to have any particular attribute.
  • You cannot constrain a generic type parameter to support any specific operator. There is therefore no way to compile the following code:

[C#]

public class Calculator<T>
{
   public T Add(T argument1,T argument2)
   {
      return argument1 + argument2; //Does not compile 
   }
   //Rest of the methods 
}

[Visual Basic]

Public Class calculator(Of T)
    Public Function add(ByVal argument1 As T, ByVal argument2 As T) As T
        Return argument1 + argument2
        ' The preceding statement does not compile.
    End Function
    ' Rest of the methods.
End Class

[C++]

generic <typename T>
public ref class Calculator
{
public: T Add(T argument1,T argument2)
   {
      return argument1 + argument2; //Does not compile 
   }
   //Rest of the methods 
};

Why Can I Not Use Enums, Structs, or Sealed Classes as Generic Constraints?

You cannot constraint a generic type parameter to derive from a non-derivable type. For example, the following does not compile:

[C#]

public sealed class MySealedClass
{...}
public class MyClass<T> where T : MySealedClass //Does not compile 
{...}

[Visual Basic]

Public NotInheritable Class MySealedClass
    ...
End Class
Public Class SomeClass(Of T As MySealedClass
' The preceding statement does not compile.
    ...
End Class

[C++]

public ref class MySealedClass sealed
{...};
generic <typename T> where T : MySealedClass
public ref class MyClass //Does not compile 
{...};

The reason is simple: The only type arguments that could possibly satisfy the above constraint is the type MySealedClass itself, making the use of generics redundant. For this very reason, all other non-derivable types such as structures and enums are not allowed in constraints.

Is Code that Uses Generics Faster than Code that Does Not?

The answer depends on the way the non-generic code is written. If the code is using objects as the amorphous containers to store items, then various benchmarks have shown that in intense calling patterns, generics yield on average 100 percent performance improvement (that is, three times as fast) when using value types, and some 50 percent performance improvement when using reference types.

If the non-generic code is using type-specific data structures, then there is no performance benefit to generics. However, such code is inherently very fragile. Writing a type-specific data structure is a tedious, repetitive, and error-prone task. When you fix 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 essentially is the same data structure.

Is an Application That Uses Generics Faster than an Application That Does Not?

Depending on the application of course, but generally speaking, in most real-life applications, bottle necks such as I/O will mask out any performance benefit from generics. The real benefit of generics is not performance but rather type safety and productivity.

How Are Generics Similar to Classic Visual C++ Templates?

Generics are similar in concept to classic C++ templates: both allow data structures or utility classes to defer to the client the actual types to use, and both offer productivity and type-safety benefits.

How Are Generics Different from Classic Visual C++ Templates?

There are two main differences: in the programming model and in the underlying implementation. In the programming model, .NET generics can provide enhanced safety compared to classic Visual C++ templates. .NET generics have the notion of constraints, which gives you added type safety. On the other hand, .NET generics offer a more restrictive programming model—there are quite a few things that generics cannot do, such as using operators, because there is no way to constraint a type parameter to support an operator. This is not the case in classic Visual C++ templates where you can apply any operator you like on the type parameters. At compile time, the classic Visual C++ compiler will replace all the type parameters in the template with your specified type, and any incompatibility is usually discovered then.

Both templates and generics can incur some code bloat, and both have mechanisms to limit that bloat. Instantiating a template with a specific set of types instantiates only the methods actually used; and then all methods that result in identical code are automatically merged by the compiler which prevents needless duplication. Instantiating a generic with a specific set of types instantiates all of its methods, but only once for all reference type arguments; bloat comes only from value types, because the CLR instantiates a generic separately once for each value type argument. Finally, .NET generics allow you to ship binaries, while C++ templates require you to share some code with the client.

What Is the Difference Between Using Generics and Using Interfaces (or Abstract Classes)?

Interfaces and generics serve different purposes. Interfaces are about defining a contract between a service consumer and a service provider. As long as the consumer programs strictly against the interface (and not a particular implementation of it), it can use any other service provider that supports the same interface. This allows switching service providers without affecting (or with minimum effect on) the client's code. The interface also allows the same service provider to provide services to different clients. Interfaces are the cornerstone of modern software engineering, and are used extensively in past and future technologies, from COM to .NET to Indigo and SOA.

Generics are about defining and implementing a service without committing to the actual types used. As such, interfaces and generics are not mutually exclusive. Far from it, they compliment each other. You can and you should combine interfaces and generics.

For example, the interface ILinkedList<T> defined as:

[C#]

public interface ILinkedList<T>
{
   void AddHead(T item);
   void RemoveHead(T item);
   void RemoveAll();
}

[Visual Basic]

Public Interface ILinkedList(Of T)
    Sub AddHead(ByVal item As T)
    Sub RemoveHead(ByVal item As T)
    Sub RemoveAll()
End Interface

[C++]

generic <typename T>
public interface class ILinkedList
{
   void AddHead(T item);
   void RemoveHead(T item);
   void RemoveAll();
};

Can be implemented by any linked list:

[C#]

public class LinkedList<T> : ILinkedList<T>
{...}

public class MyOtherLinkedList<T> : ILinkedList<T>
{...}

[Visual Basic]

Public Class LinkedList(Of T)
    Implements ILinkedList(Of T)
    ...
End Class
Public Class MyOtherinkedList(Of T)
    Implements ILinkedList(Of T)
    ...
End Class

[C++]

generic <typename T>
public ref class LinkedList : ILinkedList<T>
{...};

generic <typename T>
public ref class MyOtherLinkedList : ILinkedList<T>
{...};

You can now program against ILinkedList<T>, using both different implementations and different type arguments:

[C#]

ILinkedList<int> numbers  = new LinkedList<int>();
ILinkedList<string> names = new LinkedList<string>();

ILinkedList<int> moreNumbers = new MyOtherLinkedList<int>();

[Visual Basic]

Dim numbers As ILinkedList(Of Integer) = New LinkedList(Of Integer)()
Dim names As ILinkedList(Of String) = New LinkedList(Of String)()
Dim moreNumbers As ILinkedList(Of Integer) = New MyOtherLinkedList(Of Integer)()

[C++]

ILinkedList<int> ^numbers    = gcnew LinkedList<int>;
ILinkedList<String ^> ^names = gcnew LinkedList<String ^>;

ILinkedList<int> ^moreNumbers = gcnew MyOtherLinkedList<int>();

How Are Generics Implemented?

Generics have native support in IL and the CLR itself. When you compile generic 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 such as constraints.

The client-side compiler uses that generic metadata to support type safety. When the client provides a type arguments, the client's compiler substitutes the generic type parameter in the server metadata with the specified type. This provides the client's compiler with type-specific definition of the server, as if generics were never involved. At run time, the actual machine code produced depends on whether the specified types are value or reference type. If the client specifies a value type, 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 keeps track of type-specific server code it already generated. If the JIT compiler is asked to compile the generic server with a value type it has 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 requests for a reference type instead of a generic type parameter. Note that this way the JIT compiler only reuses actual code. Instances are still allocated according to their size off the managed heap, and there is no casting.

Why Can't I Use Operators on Naked Generic Type Parameters?

The reason is simple—Since there is no way to constrain a generic type parameter to support an operator, there is no way the compiler can tell whether the type specified by the client of the generic type will support the operator.

Consider for example the following code:

[C#]

class Node<K,T>
{
   public K Key;
   public T Item;
   public Node<K,T> NextNode;
} 

public class LinkedList<K,T> 
{
   Node<K,T> m_Head;

   public T this[K key]
   {
      get
      {
         Node<K,T> current = m_Head;
         while(current.NextNode != null)
         {
            if(current.Key == key)) //Does not compile
               break;
            else      
               current = current.NextNode;
         }
         return current.Item; 
      }
   }
   //Rest of the implementation 
}

[Visual Basic]

Class Node(Of K, T)
    Public Key As K
    Public Item As T
    Public NextNode As Node(Of K, T)
End Class

<DefaultMember("Item")> _ 
Public Class LinkedList(Of K, T)
    
    Dim m_Head As Node(Of K, T)

    Public ReadOnly Property Item(ByVal key As K) As T
        Get
            Dim current As Node(Of K, T) = m_Head
            While current.NextNode IsNot Nothing
                If current.key = key Then
            ' The preceding statement does not compile.
                    Exit While
                Else
                    current = current.NextNode
                End If
            End While
            Return current.item
        End Get
    End Property
    ' Rest of the implementation
End Class

[C++]

generic <typename K, typename T>
ref class Node
{
public: K Key;
        T Item;
        Node<K,T> ^NextNode;
};

generic <typename K, typename T>
public ref class LinkedList 
{
public:
   Node<K,T> ^m_Head;
public:
   property T default[]
   {
      T get(K key)
      {
         Node<K,T> ^current = m_Head;
         while(current->NextNode)
         {
            if(current->Key == key)) //Does not compile
               break;
            else      
               current = current->NextNode;
         }
         return current->Item; 
      }
   }
   //Rest of the implementation 
};

The compiler will refuse to compile this line:

[C#]

if(current.Key == key))

[Visual Basic]

If current.key = key Then

[C++]

if(current->Key == key))

Because it has no way of knowing whether the type the consumer will specify will support the == operator.

When Can I Use Operators on Generic Type Parameters?

You can use an operator (or for that matter, any type-specific method) on generic type parameters if the generic type parameter is constrained to be a type that supports that operator. For example:

[C#]

class MyOtherClass
{
   public static MyOtherClass operator+(MyOtherClass lhs,MyOtherClass rhs)
   {
      MyOtherClass product = new MyOtherClass();
      product.m_Number = lhs.m_Number + rhs.m_Number;
      return product;
   }
   int m_Number;
   //Rest of the class 
}

class MyClass<T> where T : MyOtherClass
{
   MyOtherClass Sum(T t1,T t2)
   {
      return t1 + t2;
   }
}

[Visual Basic]

Class MyOtherClass
   Public Shared Function op_Addition(ByVal lhs As MyOtherClass, 
                                      ByVal rhs As MyOtherClass) As MyOtherClass
      Dim product As New MyOtherClass
      product.m_Number =  lhs.m_Number + rhs.m_Number
      return product
   End Function   
   Private m_Number As Integer
End Class

Class SomeClass(Of T As MyOtherClass)
   Private Function Sum(ByVal t1 As T, ByVal t2 As T) As MyOtherClass
      Return (t1 + t2)
   End Function
End Class

Can I Use Generic Attributes?

You cannot define generic attributes:

[C#]

//This is not possible:
class MyAttribute<T>: Attribute
{...}

[Visual Basic]

' The following declaration is not possible.
Public Class MyAttribute(Of T)
    Inherits Attribute
    ...
End Class

[C++]

//This is not possible:
generic <typename T>
ref class MyAttribute: Attribute
{...};

However, nothing prevents you from using generics internally, inside the attribute's implementation.

Are Generics CLS Compliant?

Yes. With the release of .NET 2.0, generics will become part of the CLS.

 

About the author

Juval Lowy is a software architect and the principal of IDesign, specializing in .NET architecture consulting and advanced .NET training. Juval is Microsoft's Regional Director for the Silicon Valley, working with Microsoft on helping the industry adopt .NET. His latest book is Programming .NET Components 2nd Edition (O'Reilly, 2005). Juval participates in the Microsoft internal design reviews for future versions of .NET. Juval published numerous articles, regarding almost every aspect of .NET development, and is a frequent presenter at development conferences. Microsoft recognized Juval as a Software Legend as one of the world's top .NET experts and industry leaders.

Show:
© 2014 Microsoft