Current .Net Version: 2.0.50727
Home
Articles
C# & .Net Framework Reference
Practice C# Online
XML Web Services
RSS Feeds
Code Snippets
T-SQL Scripts
Videos
Valuable Links
Contact Us
Mike's C#, VS.NET and SQL Blog




ROR XML Info.
What's New in C# 2.0 - Generics
Author: Michael G.     Last Updated:9/29/2005 8:03:35 PM
Category(ies): C# 2.0, .Net Framework 2.0
Description: Learn all about the new C# 2.0 Framework feature: Generics.
  Add & View Comments About this Article

Tested with Microsoft Visual C# 2005 Beta 1, .Net 2.0.40607.42

What are Generics?

Generics is a term used to describe generic types. Generics are simply placeholders for actual types. Generics are defined with left and right brackets: <T>. The T in <T> is simply a name that describes the type that is going to be used. T is the standard name used to define a generic type in most of Microsoft's documentation.

You can create generic classes, delegates, interfaces and methods: // Examples of various places where generics can be defined: // Generic Class: public class MyGenericClass<T> {} // Generic Method: public void MyGenericMethod<T>(T type) {} // Generic Interface: public interface MyGenericInterface<T> {} // Generic Delegate: public delegate void MyGenericDelegate<T>(T type); The following example illustrates how we can define a single generic class that's used as a string and an integer: namespace WhatsNew20Generics { class Program { static void Main(string[] args) { MyGeneric<int> myGeneric = new MyGeneric<int>(90210); MyGeneric<String> myGeneric2 = new MyGeneric<String>("Hello World!"); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } } // T is the generic type: public class MyGeneric<T> { public MyGeneric(T type) { // We don't know what the type is, but we'll find out here: Console.WriteLine("Type of 'T' is {0}.", typeof(T).ToString()); } } /* The following console output should be produced when this application is executed: Type of 'T' is System.Int32. Type of 'T' is System.String. Press any key to continue... */ } So, we can take a generic class and and transform it into other types without having to explicitly define those types through seperate classes.

Generics in the .Net 2.0 FCL

The new 2.0 FCL (Framework Class Library) contains a number of generic collection classes that are now type-safe. Prior to version 2.0, we had to derive from a standard object-based collection, such as ArrayList, and then box and unbox objects when storing and retrieving its values. That's no longer necessary. We can now create type-safe collections from generic classes in the 2.0 FCL System.Collections.Generic namespace:

Generic Class/Interface Description Non-Generic Equivalent
Collection<T>, ICollection<T> Provides the base class for a generic collection. CollectionBase, ICollection
Comparer<T>, IComparer<T>, IComparable<T> Compares two objects of the same generic type for equivalence and for sorting. Comparer, IComparer, IComparable
Dictionary<K, V>, IDictionary<K,V> Represents a collection of key/value pairs that are organized based on the key. Hashtable, IDictionary
Dictionary<K, V>.KeyCollection Represents the collection of keys in a Dictionary<K, V>. None.
Dictionary<K, V>.ValueCollection Represents the collection of values in a Dictionary<K, V>. None.
IEnumerable<T>, IEnumerator<T> Represents a collection that can be iterated using foreach. IEnumerable, IEnumerator
KeyedCollection<T, U> Represents a keyed collection. KeyedCollection
LinkedList<T> Represents a doubly linked list. None.
LinkedListNode<T> Represents a node in a LinkedList<T>. None.
List<T>, IList<T> Implements the IList<T> interface using an array whose size is dynamically increased as required. ArrayList, IList
Queue<T> Represents a first-in, first-out collection of objects. Queue
ReadOnlyCollection<T> Provides the base class for a generic read-only collection. ReadOnlyCollectionBase
SortedDictionary<K, V> Represents a collection of key/value pairs that are sorted by key based on the associated IComparer<T> implementation. SortedList
Stack<T> Represents a simple last-in-first-out (LIFO) collection of objects. Stack

The Benefits of Using Generics versus .Net 1.0 and 1.1 Collections

So, gone are the days in which us programmers had to create type specific collections or make use of costly casting, boxing and unboxing techniques. We can now take advantage of Generics to do this for us. That said, you probably won't even have a necessity to build your own generics. Instead, you'll probably use the various FCL generics in .Net 2.0.

Let's take a look at how we use to store data in an ArrayList and how we can now do the same thing with a generic equivalent, Collection<>:

System.Collections.ArrayList storage method: System.Collections.ArrayList arrayList = new System.Collections.ArrayList(); // 100 integer values will be stored as // objects in the ArrayList by casting/boxing: for (int i = 1; i <= 100; i++) arrayList.Add(i); // Now, let's retrieve the values from each type of collection: // Retrieve the values from the ArrayList: foreach (int i in arrayList) { // the Objects in arrayList are // cast to Int32 implicitly. } System.Collections.Generic.Collection<> storage method: System.Collections.Generic.Collection<int> genericCollection = new System.Collections.Generic.Collection<int>(); // 100 integer values will be stored as // Int32 in the Collection<> without casting/boxing: for (int i = 1; i <= 100; i++) genericCollection.Add(i); // Retrieve the values from the Collection<>: foreach (int i in genericCollection) { // No need for the CLR to cast because // the underlying values are already Int32. }
So, the benefits of using Generics as opposed to .Net 1.0 and 1.1 Collections are apparent: there's no need for casting and boxing between objects and specific types.

Generic Constraints

You can add constraints (or rules) to generic classes, interfaces, delegates and methods. Constraints are enabled by adding the keyword where next to the type parameter (<T>): namespace WhatsNew20Generics { class Program { static void Main(string[] args) { // This will work because int is a struct: MyGenericClass<int>.MyMethod(); // This will cause an exception because // String is a class: //MyGenericClass<String>.MyMethod(); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } // This Generic will enforce a constraint that requires // the type is a value type: public class MyGenericClass<T> where T : struct { public static void MyMethod() { Console.WriteLine("MyGenericClass type is {0}", typeof(MyGenericClass<T>).ToString()); } } /* The Following console output should be produced: MyGenericClass type is WhatsNew20Generics.Program+MyGenericClass`1[System.Int32] Press any key to continue... */ } } Generic constraints make it possible for the CLR to be able to compare generic types. Without them, there'd be no way for the CLR to know whether two types can be compared against eachother: namespace WhatsNew20Generics { class Program { static void Main(string[] args) { MyGenericClassConstrained<String>.Compare("Hello World!", "Hello World!"); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } // This Generic will enforce a constraint that requires // the type is a reference type: public class MyGenericClassConstrained<T> where T : class { public static void Compare(T someType, String someString) { if (someType == someString) Console.WriteLine("someType is equal to someString."); else Console.WriteLine("someType is not equal to someString"); Console.WriteLine("someType = {0}", someType.ToString()); } } // This Generic will does not use a constraint and // the compare operator == will throw a compile-time // exception: /* Error 2 Operator '==' cannot be applied to operands of type 'T' and 'string' WhatsNew20Generics\Program.cs 41 20 */ public class MyGenericClass<T> { public static void Compare(T someType, String someString) { // This will cause a compile-time exception: if (someType == someString) Console.WriteLine("someType is equal to someString."); else Console.WriteLine("someType is not equal to someString"); Console.WriteLine("someType = {0}", someType.ToString()); } } } } .Net 2.0 includes the following types of generic constraints:

Constraint Description
where T: struct The type argument must be a value type.
where T : class The type argument must be a reference type.
where T : new() The type argument must have a public parameterless constructor. When used in conjunction with other constraints, the new() constraint must be specified last.
where T : <base class name> The type argument must be or derive from the specified base class.
where T : <interface name> The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.

Using the default Keyword to Assign Default Values for Generic Types

When building generic classes, be careful when using default generic types because you won't be able to know for sure whether the underlying value will be a value or reference type. The default value for a value type is zero and the default value type for a reference type is null. Assigning a default value to a type you don't know whether is a value or reference can become a problem. Do you assign an unknown type a zero or a null?

To overcome this issue, you can use the default keyword. default returns an instance variable of the unknown generic type and its value will be 0 if the type is a value type and null if the type is a reference type. /***** WITHOUT default KEYWORD *****/ public class MyClass { public static T GetDefaultValue<T>() { // Hmm, we don't know what to return because // we don't know if we're dealing with a // value or reference type! What to do? // Do we return 0 or null? // We can't return 0 for a reference type // and we can't return null for a value type! // This will give us a compile-time error: //T result = null; // This will also give us a compile-time error: //result = 0; } } /***** WITH default KEYWORD *****/ public class MyClassWithDefault { public static T GetDefaultValue<T>() { T result = default(T); // Will return 0 if a value type and // null if a reference type: return result; } }

Comments Click Here to Add your Comment
 
COPYRIGHT/DISCLAIMER | CONTACT US