Generics in C#
I have started reading up on Generic in C# 2.0. I can see why people are going gaga over this.
Salient points [learnt/copied from MSDN]:
- Runtime implemented than compiler supported.
This means you will need to make sure that your application, when installed, will need to deploy .NET 2.0 framework.
- Types of constraints you can place in generics are many.
- You can use “where” to apply base class constraint. Some specialized ones are,
Public Parameterless constructor must exist
where T:<base class name>
Derives from base class
where T:<interface name>
Derives from interface
- If you don’t place constraints,
i. The != and == operators cannot be used because there is no guarantee that the concrete type argument will support these operators.
ii. They can be converted to and from System.Object or explicitly converted to any interface type.
iii. You can compare to null. If an unbounded parameter is compared to null, the comparison will always return false if the type argument is a value type.
- Constraining using a generic type is a naked type constraint. Supported in C#.
- For most collections, there are implementations in the framework itself. If you want to create your own, follow the following rules(Heed to these advices, and you shall prosper).
- Which types to generalize into type parameters. As a general rule, the more types you are able to parameterize, the more flexible and reusable your code becomes. Too much generalization can result in code that is difficult for other developers to read or understand.
- What constraints, if any, to apply to the type parameters. A good rule is to apply the maximum constraints possible that will still let you handle the types you need to handle. For example, if you know that your generic class is intended for use only with reference types, then apply the class constraint. That will prevent unintended use of your class with value types, and will enable you to use the as operator on T, and check for null values.
- Whether to factor generic behavior into base classes and subclasses. Since generic classes can serve as base classes, the same design considerations apply here as with non-generic classes. See below for rules on inheriting from generic base classes.
- Whether to implement one or more generic interfaces. For example, if you are designing a class that will be used to create items in a generics-based collection, you may need to implement an interface such as IComparable<T> where T is the type of your class.
- Rules of derivation for classes. Most rules here are common-sense.
- You can derive generic classes a) from other non-generic classes, b) derive from open constructed type (List<T>) or c) from other closed constructed type (List<int>).
- Concrete classes can only inherit from closed constructed type.
- Generic classes, if they derive from other generic classes and hide some of the type arguments, will need to supply all the type arguments required by the base generic class.
- All constraints from the base generic class must be implied / expressed in the derived generic class.
- When deriving from a generic interface, use the generic version of the interface. i.e. If you are going to use CompareTo(), use IComparable<T> instead of IComparable.
- Rules of derivation for interfaces. Not much difference between this and classes.
- Concrete classes can derive from closed constructed interface type.
- Generic methods can be overloaded on the number of type parameters. Type inference takes place at compile time before the compiler attempts to resolve any overloaded method signatures. The compiler applies type inference logic to all generic methods that share the same name. So there are other tricks can also be placed in the constraints to have methods called when certain constraint is met.
- There are generic delegates. Check MSDN for more info. Pretty cool.
- The keyword default can be used to handle a case in generic implementation when the T needs to be compared for being “null” or 0 based on it being a reference type or a value type.
- Interesting list of differences between generics in C# and templates in C++.
- No non-type template params.
- No explicit or partial specialization.
- Type as the base class. (The way ATL solved many a problem).
- No defaults for type params.
- C++ allows code that might not be valid for all type parameters in the template, which is then checked for the specific type used as the type parameter. C# requires code in a class to be written in such a way that it will work with any type that satisfies the constraints. For example, in C++ it is possible to write a function that uses the arithmetic operators + and – on objects of the type parameter, which will produce an error at the time of instantiation of the template with a type that does not support these operators. C# disallows this; the only language constructs allowed are those that can be deduced from the constraints.
- Generics, when created at runtime, work differently between value types and reference types. For value types, the runtime ends up creating the generic type for every specialized class. i.e. you will get a type generated for List<int> and one for List<long>. But, on the other hand, if you use List<ObjectA> and List<ObjectB>, the number of generic types constructed is only one and its reused for all other reference type based generic. Interesting.
Next, I will see what I can read up on VB.NET’s generic (oh, its there and it uses Of type syntax).