An Introduction to Generics...
Generics are a new, very powerful, addition to the 2.0 version of the .NET framework...
Generics are a new, very powerful, addition to the 2.0 version of the .NET framework. Through a new syntax, generics add support to creating strongly-typed properties, methods, classes, fields, events, and collection classes. Let's take a look at an example:
|
public class MyClass<T> { } |
This class defines a generic type T that can be used as a regular type throughout the class definition. At design-time, the type is not known; it isn’t assigned until the class is instantiated. More on that later. A property definition could look like this:
|
private T _data; public T Data { get { return _data; } set { _data = value; } } |
Both the property type and variable type are both strongly-typed through this mechanism. This syntax also works for methods, which the method will return, or pass in, an appropriate type:
public shared T GetData(T originalData) { .. }
So how does T get assigned anything? Let's look at how the class is instantiated:
private MyClass<string> _class = new MyClass<string>();
Whenever the class is created, all instances of T above are replaced with type string, as defined by the parser using the <> syntax (VB.NET uses the (Of ..) syntax). So the type is defined globally throughout the class and all instances of T are replaced with string, which is very useful for developers, especially in the area of collections. For example, a custom collection before looked like:
|
public class MyCollection : CollectionBase { public void Add(MyItem item) { List.Add(item); } ... public void Remove(MyItem item) { List.Remove(item); } } |
You had to create all of these methods because List works with base type object, and as such, no strong typing was available without creating the custom class above. With Generics, the above class with all of the features (and much more) can be easily created in one line:
List<MyItem> _list = new List<MyItem>();
List is one of the many classes available in System.Collections namespace that are already available for you. List<> has Add, Contains, Insert, IndexOf, Remove, and many other collection-oriented methods already defined for you. All you do is provide a type, and you can use it for anything, and this is out-of-the-box. In addition, custom collections are easily creatable using the generics constructs, so if you have some special needs for a collection, you can create one yourself.
Generics can also be used for classes as well. Another predefined class is the KeyValuePair object, which has a definition of KeyValuePair<TKey, TValue>. To instantiate this object, you would do:
KeyValuePair<string, MyItem> pair = new KeyValuePair<string, MyItem>();
This object isn't a collection, but contains one key/value pair, which you could add to a List<> object. Other useful objects are the Dictionary objects, containing a list of key/value pairings, or the collection object, which is a scaled-down List class (exposing less methods), to name a few.
It is possible to inherit from a generic class, and pass in the dynamic type to the base class, as such:
public class MyCollection<T> : List<T> { .. }
The type provided when instantiating MyCollection will be passed to the List class that it inherited from. Properties and methods can also define their own generic types, independent of any class declaration. The class doesn’t even need to implement generics at all to implements its own generic. Methods define the generic type at the end of the method (for example, Sort<T>) but before the parameters. The following is a declaration for a method that serializes data:
|
public static void Serialize<T>(T obj, string path) { XmlSerializer ser = null; FileStream fs = null; try { ser = new XmlSerializer(typeof(T)); fs = new FileStream(path, FileMode.Create); ser.Serialize(fs, obj); } finally { if (fs != null) fs.Dispose(); ser = null; } } |
Events can define generic values also; the delegate can define a method itself, or one of the parameters in the event can be a generic class. This can be very useful; for instance, EventHandler is also defined as a generic, which allows you to provide an event argument dynamically.
public static event EventHandler<FileEventArgs> OnFileUpdated;
The last topic on generics that I'll cover is constraints. Sometimes, when using a generic, only a certain number of types are allowed. For example, a generic class may only want to allow a string or an integer as the types used for the class. Another example would be a mathematics class may only want numerical types (integer, float, etc.), and such would want to provide a constraint restricting the allowable values to numerical types. This is possible through a where constraint, as illustrated below:
public abstract class CollectionBase<T> : List<T> where T:ItemBase
{..}
"Where T" specifies the generic to replace throughout the class. The colon is a required character, followed by the type, or allowable types (separated by a comma). When inheriting, where constraints should be at the very end. For classes, classes that inherited from the base class (in the example above, all classes that inherit from ItemBase) are included in this collection definition. For more information on constraints, see ms-help://MS.VSExpressCC.v80/MS.NETFramework.v20.en/dv_csref/html/141b003e-1ddb-4e1c-bcb2-e1c3870e6a51.htm. One more point about classes; a new() keyword can be used as a constraint, saying that any object that can be instantiated is allowed, which includes all classes and objects, etc. other than primitive types like string, int, etc.
Lastly, when multiple types are allowed, especially in mixing value and reference types, the default keyword will return a null for reference types and zero for integer types. For structure types, it will use null or zero for the parameters based on each parameter type. See ms-help://MS.VSExpressCC.v80/MS.NETFramework.v20.en/dv_csref/html/b9daf449-4e64-496e-8592-6ed2c8875a98.htm for more information.
Generics are a very useful mechanism in developing collections and other objects rather quickly. They have many different uses and the flexibility and strong-typing provided makes it a powerful tool.