Chapter 11. Generics

Support for generics is one of the nicest features of C# and .NET. Generics allow you to create open-ended types that are converted into closed types at runtime. Each unique closed type is a unique type. Only closed types can be instantiated. When you declare a generic type, you specify a list of type parameters in the declaration for which type arguments are given to create closed types, as in the following example:

public class MyCollection<T>
{
    public MyCollection() {
    }

    private T[] storage;
}

In this case, I declared a generic type, MyCollection<T>, which treats the type within the collection as an unspecified type. In this example, the type parameter list consists of only one type, and it is described with syntax in which the generic types are listed, separated by commas, between angle brackets. The identifier T is really just a placeholder for any type. At some point, a consumer of MyCollection<T> will declare what's called a closed type, by specifying the concrete type that T is supposed to represent. For example, suppose that some other assembly wants to create a MyCollection<T> constructed type that contains members of type int. Then it would do so as shown in the following code:

public void SomeMethod() {
    MyCollection<int> collectionOfNumbers = new MyCollection<int>();
}

MyCollection<int> in this example is the closed type. MyCollection<int> can be used just like any other declared type, and it also follows all the same rules that other nongeneric types follow. The only difference is that it was born from a generic type. At the point of instantiation, the IL code behind the implementation of MyCollection<T> is JIT-compiled in a way that all the usages of type T in the implementation of MyCollection<T> are replaced with type int.

Note that all unique constructed types created from the same generic type are, in fact, completely different types that share no implicit conversion capabilities. For example, MyCollection<long> is a completely different type than MyCollection<int>, and you cannot do something like the following:

// THIS WILL NOT WORK!!!
public void SomeMethod( MyCollection<int> intNumbers ) {
    MyCollection<long> longNumbers = intNumbers;       // ERROR!
}

If you're familiar with the array covariance rules that allow you to do the following, you'll be happy to know that C# 4.0 adds new syntax to let you do this with generics:

public void ProcessStrings( string[] myStrings ) {
    object[] objs = myStrings;
    foreach( object o in objs ) {
        Console.WriteLine( o );
    }
}

The difference is that with array covariance, the source and the destination of the assignment are of the same type: System.Array. The array covariance rules simply allow you to assign one array from another, as long as the declared type of the elements in the array is implicitly convertible at compile time. However, in the case of two constructed generic types, they are completely separate types.

Starting with C# 4.0, the language supports covariance and contravariance between generic interfaces and delegates with reference-based type arguments. This helps relax the restrictions on implicit convertibility for some generic types and helps to create code that simply makes sense without a lot of extra conversion baggage. I will have more to say about the topic of variance in the section titled "Co- and Contravariance" later in the chapter.

Difference Between Generics and C++ Templates

It's no accident that the syntax of generics is similar to that of C++ templates, when the syntax for every other element in C# is based on the corresponding C++ syntax. This approach makes the language look familiar to many. As is typical throughout C#, the designers have streamlined the syntax and removed some of the verbosity. However, the similarities end there because C# generics behave very differently from C++ templates, and if you come from the C++ world, you must make sure that you understand the differences. Otherwise, you might find yourself attempting to apply your C++ template knowledge in ways that simply won't work with generics.

The main difference between the two is that expansion of generics is dynamic, whereas expansion of C++ templates is static. In other words, C++ templates are always expanded at compile time. Therefore, the C++ compiler must have access to all template types—generally through header files—and any types used to create the closed types from the template types at compile time. For this reason alone, it is impossible to package C++ templates into libraries. I know that many developers become confused by this fact when learning C++ templates for the first time. I remember plenty of times when it would have been nice to be able to package a C++ template into a static library or a DLL. Unfortunately, that is not possible. That's why all the code for C++ template types usually lives in headers. This makes it difficult to package proprietary library code within C++ templates because you must essentially give your code away to anyone who needs to consume it. The STL is a perfect example: notice that almost every bit of your favorite STL implementation exists in header files.

Generics, on the other hand, can be packaged in assemblies and consumed later. Instead of being formed at compile time, constructed types are formed at runtime, or more specifically, at JIT-compile time. In many ways, this makes generics more flexible. However, as with just about anything in the engineering world, advantages come with disadvantages. You must treat generics significantly differently at design time from C++ templates, as you'll see at the end of this chapter.

Note

Each time the JIT compiler forms a closed type, a new type is initialized for the application domain that uses it. Naturally, this places a demand on the memory consumption of the application, also known as the working set. Once a type is initialized and loaded into an application domain, you cannot uninitialize and unload it without destroying the application domain as well. Under some rare circumstances, you might need to consider these ramifications when designing systems that use generics. In general, though, such concerns are typically minimal. If your generic type declares a lot of static fields, creating many closed types from it could place pressure on memory because each closed type gets its own copy of those static fields. Additionally, if those closed types are used in multiple application domains, there will be a copy of that static data for each application domain the type is loaded into.

Efficiency and Type Safety of Generics

Arguably, the added efficiency when using value types in collections is one of the greatest gains from generics in C#. Whereas a regular array based on System.Array can contain a heterogeneous collection of instances created from many types as long as it holds references to a common base type such as System.Object, it does come with its drawbacks. Take a look at the following usage:

public void SomeMethod( ArrayList col ) {
    foreach( object o in col ) {
        ISomeInterface iface = (ISomeInterface) o;
        o.DoSomething();
    }
}

Because everything in the CLR is derived from System.Object, the ArrayList passed in via the col variable could possibly contain a hodgepodge of things. Some of those things might not actually implement ISomeInterface. As you'd expect, an InvalidCastException could erupt from this code. However, wouldn't it be nice to be able to utilize the C# compiler's type engine to help sniff out such things at compile time? That's exactly what generics allow you to do. Using generics, you can devise something like the following:

public void SomeMethod( IList<ISomeInterface> col ) {
    foreach( ISomeInterface iface in col ) {
        o.DoSomething();
    }
}

Here, the method accepts an interface of IList<T>. Because the type parameter to the constructed type is of type ISomeInterface, the only objects the list can hold are those of type ISomeInterface. Instantly, the compiler has a bigger stick to wield while enforcing type safety.

Note

Added type safety at compile time is always a good thing because it's much better to capture bugs based on type mismatches earlier at compile time rather than later at runtime.

You could have solved the same problem without using generics, but it would have required writing a class by hand that would have served the same purpose as the List<ISomeInterface> constructed type. Thus, another beauty of generics is similar to that of C++ templates: They provide an easy-to-specialize shell for new types to be built from thus increasing code re-use.

The compiler is your friend, and you should always provide it with as much type information as possible to help it do its job. Because everything in C# and the CLR derives from System.Object one way or another, you can easily cast away all type information from objects, thus crippling the compiler. If you come from a C++ environment, just imagine how ugly things could get if you preferred to pass around pointers to objects as void*. And that's not even mentioning the hard-to-find bugs that would come from such madness.

The example you've just seen shows how to use generics for better type safety. However, you haven't really gained much yet from an efficiency standpoint. The real efficiency gain comes into play when the type argument is a value type. Remember that a value type inserted into a collection in the System.Collections namespace, such as ArrayList, must first be boxed because the ArrayList maintains a collection of System.Object types. An ArrayList meant to hold nothing but a bunch of integers suffers from severe efficiency problems because the integers must be boxed and unboxed each time they are inserted and referenced or extracted from the ArrayList, respectively. Also, an unboxing operation in C# is normally formed with an IL unbox operation paired with a copy operation on the value type's data. Additionally, all that boxing puts more pressure on the managed heap. Generics come to the rescue to stop this madness. As an example, compile the following code and then load the assembly into ILDASM to compare the IL generated for each of the methods that accept a Stack instance:

using System;
using System.Collections;
using System.Collections.Generic;

public class EntryPoint
{
    static void Main() {
    }

    public void NonGeneric( Stack stack ) {
        foreach( object o in stack ) {
            int number = (int) o;
            Console.WriteLine( number );
        }
    }

    public void Generic( Stack<int> stack ) {
        foreach( int number in stack ) {
            Console.WriteLine( number );
        }
    }
}

Notice that the IL code generated by the NonGeneric method has at least ten more instructions than the generic version. Most of this is attributed to the type coercing and unboxing that the NonGeneric method must do. Furthermore, the NonGeneric method could possibly throw an InvalidCastException if it encounters an object that cannot be explicitly cast and unboxed into an integer at runtime.

Clearly, generics offer the compiler much greater latitude to help it do its job by not stripping away precious type information at compile time. However, you could argue that the efficiency gain is so high that the primary motivator for generics in the CLR was to avoid unnecessary boxing operations. Either way, both gains are extremely significant and worth utilizing to the fullest extent.

Generic Type Definitions and Constructed Types

As I touched upon previously, a generic type is a compiled type that is unusable until a closed type is created from it. A nongeneric type is also known as a closed type, whereas a generic type is known as an open type. It is possible to define a new open type via a generic, as shown in the following example:

public class MyClass<T>
{
    private T innerObject;
}

public class Consumer<T>
{
    private MyClass< Stack<T> > obj;
}

In this case, a generic type, Consumer<T>, is defined and also contains a field that is based on another generic type. When declaring the type of the Consumer<T>.obj field, MyClass< Stack<T> > remains open until someone declares a constructed type based on Consumer<T>, thus creating a closed type for the contained field.

Generic Classes and Structs

So far, all the examples I've shown you have been generic classes; in fact, the most common types of generic declaration you will use are generic classes and structs. Also, I've been running pretty fast and loose with my terminology, so I'll be more explicit from now on.

Overall, declarations of all generic struct and class types follow the same rules as those for regular struct and class types. Any time a class declaration contains a type parameter list within angle brackets, it is a generic type from that point on. Likewise, any nested class declaration—whether it's generic or not—that is declared within the scope of a generic type is a generic type because the nested type's fully qualified name requires a type argument to completely specify the nested type.

Generic types are overloaded based upon the number of arguments in their type argument lists. The following example illustrates what I mean:

public class Container {}
public class Container<T> {}
public class Container<T, R> {}

Each of these declarations is valid within the same namespace. You can declare as many generic types based on the Container identifier as you want, as long as each one has a different count of type parameters. Within the scope of the definitions shown previously, you cannot declare another type named Container<X, Y>, even though the identifiers used in the type parameters list have different names. The name-overloading rules for generic declarations are based on the count of type parameters instead of the names given to their placeholders.

When you declare a generic type, you're declaring what is called an open type. It's called that because its fully specified type is not yet known. When you declare another type based upon the generic type definition, you're declaring what's called a constructed type, as shown here:

public class MyClass<T>
{
    private Container<int> field1;
    private Container<T>   field2;
}

Both fields in the previous declaration of MyClass<T> are constructed types because they declare a new type based upon the generic type Container<T>. Container<int> is a closed type because all given type arguments are themselves closed; that is, nongeneric. However, not every constructed type is a closed type. Only field1 is of a closed type, whereas field2 is of an open type because its final type must still be determined at runtime based on the type arguments from MyClass<T>.

In C#, all identifiers are declared and are valid within a specific scope. Within the confines of a method, for example, any local variable identifiers declared within the curly braces of the method are available only within that scope. Similar rules exist for type parameter identifiers within generics. In the previous example, the identifier T is valid only within the scope of the class declaration itself. Consider the following nested class example:

public class MyClass<T>
{
    public class MyNestedClass<R>
    {
    }
}

The identifier R is valid only within the scope of the nested class, and you cannot use it within the outer scope of the declaration for MyClass<T>. However, you can use T in the nested class because the nested class is defined within the scope within which T is valid. It is generally considered to be bad form to hide outer argument identifiers within nested scopes, just as it is with variable name identifiers within nested execution scopes. For example, try to follow this confusing code:

public class MyClass<T>
{
    public class MyNestedClass<T>
    {
    }

    private Container<T> field1;

    static void Main() {
        // What does this mean for MyNestedClass?
        MyClass<int> closedTypeInstance = null;
    }
}

When the closed type MyClass<int> is declared in Main, what does it mean for the nested type? The answer is nothing. Even though the MyNestedClass<T> declaration uses the same type argument, it does not expand into the following:

// This is NOT what happens!
public class MyClass<int>
{
    public class MyNestedClass<int>
    {
    }

    private Container<int> field1;
}

Just because the type parameter for the MyClass<T> type has been specified, it does not mean that the MyNestedClass<T> has been specified as well. In fact, it would be more accurate to describe the resultant MyClass<int> as follows:

public class MyClass<int>
{
    public class MyNestedClass<T>
    {
    }

    private Container<int> field1;
}

MyNestedClass<T> still remains open, even though it used the same identifier in its parameter list as the containing type. What's actually happening is that within the curly braces of MyNestedClass<T>, the outer type argument to MyClass<T> is hidden from access by the identifier of the inner scope. It is better to declare it as follows:

public class MyClass<T>
{
    public class MyNestedClass<R>
    {
        private T innerfield1;
        private R innerfield2;
}

    private Container<T> field1;

    static void Main() {
        MyClass<int> closedTypeInstance = null;
    }
}

Now the declaration scope of MyNestedClass<R> has access to both the T and R type parameters, as illustrated. One thing worth pointing out is that even though the variable of type MyClass<int>, a closed type, is declared, it does not imply that any closed types from MyNestedClass<R> have been declared.

Generic structs and classes, just like normal structs and classes, can contain static types. However, each closed type based on the generic type contains its own instances of those static types. When you consider that each closed type is a separate concrete type, this fact makes perfect sense. For example, if MyClass<T> declares a static field named MyValue, then MyClass<int>, a closed type, has its own static field MyClass<int>.MyValue, which is unrelated to the static field MyClass<long>.MyValue. Therefore, if you need to share static data between different closed types based on the same generic type, you must devise some other means to do so. One technique involves a separate, nongeneric type that contains static data that is referenced by the generic types. Such a device is typically implemented with the Singleton pattern. You could also achieve this by having the generic type derive from a nongeneric type and put the shared statics in the nongeneric type.

Note

Keep in mind that generic types with static initializers require that the initialization code be run each and every time the CLR creates a closed type based upon the generic type. Complex type initializers, or static constructors, can possibly increase the working set of the application if too many closed types are created based upon such a generic type. For example, if you create a sizable per-type data structure in a generic type initializer, you could create a hidden source of memory consumption if many types are formed from it.

Generic Interfaces

Along with classes and structs, you can also create generic interface declarations. This concept is a natural progression from struct and class generics. Naturally, a whole host of interfaces declared within the .NET 1.1 base class library make excellent candidates to have generic versions fashioned after them. A perfect example is IEnumerable<T>. Generic containers create much more efficient code than nongeneric containers when they contain value types because they avoid any unnecessary boxing. It's only natural that any generic enumerable interface must have a means of enumerating the generic items within. Thus, IEnumerable<T> exists, and any enumerable containers you implement yourself should implement this interface. Alternatively, you could get it for free by deriving your custom containers from Collection<T>.

Note

When creating your own custom collection types, you should derive them from Collection<T> in the System.Collections.ObjectModel namespace. Other types, such as List<T>, are not meant to be derived from and are intended as a lower-level storage mechanism. Collection<T> implements protected virtual methods that you can override to customize its behavior, whereas List<T> does not.

Generic Methods

C# supports generic methods. Any method declaration that exists within a struct, a class, or an interface can be declared as a generic method. That includes static as well as virtual or abstract methods. Also, you can declare generic methods on either generic or nongeneric types. To declare a generic method, simply append a type parameter list within angle brackets to the end of the method name but before the parameter list for the method. You can declare any of the types in the method parameter list, including the method return type, using one of the generic parameters. As with nested classes, it is bad form to hide outer type identifiers by reusing the same identifier in the nested scope, which in this case, is the scope of the generic method. Let's consider an example in which a generic method might be useful. In the following code, I created a container to which I want to add the contents of another generic container:

using System;
using System.Collections.Generic;

public class MyContainer<T> : IEnumerable<T>
{
    public void Add( T item ) {
        impl.Add( item );
    }

    // Converter<TInput, TOutput> is a new delegate type introduced
    // in the .NET Framework 2.0 that can be wired up to a method that
    // knows how to convert the TInput type into a TOutput type.
    public void Add<R>( MyContainer<R> otherContainer,
                        Converter<R, T> converter ) {
        foreach( R item in otherContainer ) {
            impl.Add( converter(item) );
        }
    }

    public IEnumerator<T> GetEnumerator() {
        foreach( T item in impl ) {
            yield return item;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }

    private List<T> impl = new List<T>();
}

public class EntryPoint
{
static void Main() {
        MyContainer<long> lContainer = new MyContainer<long>();
        MyContainer<int> iContainer = new MyContainer<int>();

        lContainer.Add( 1 );
        lContainer.Add( 2 );
        iContainer.Add( 3 );
        iContainer.Add( 4 );

        lContainer.Add( iContainer,
                        EntryPoint.IntToLongConverter );

        foreach( long l in lContainer ) {
            Console.WriteLine( l );
        }
    }

    static long IntToLongConverter( int i ) {
        return i;
    }
}

First of all, take note of the syntax of the generic Add<R> method, and also notice that there are two overloads of Add in MyContainer<T>. Clearly, you need to have a method to add instances of type T—thus, the need for Add( T ). However, it would be really handy to be able to add an entire range of objects from another closed type formed from MyContainer<T>, as long as the enclosed type of the source container is convertible to the enclosed type of the target. If you look at Main, you can see the intent here. I want to place the objects contained within an instance of MyContainer<int> into an instance of MyContainer<long>. Therefore, I created a generic method, Add<R>, to allow me to accept another container that contains any arbitrary type. Because the generic class uses T in its argument list, the generic method uses R to avoid any type parameter hiding.

This technique involves a twist, though. Logically, what I'm trying to do makes perfect type sense. I want to add a collection of ints to a collection of longs, and I know that an int is easily implicitly convertible to a long, so I should be able to do this. Although this is true, you have to take into consideration that generics are formed dynamically at runtime. And at runtime, there is no guarantee what closed type formed from MyContainer<T> the Add<R> method will see. It could be MyContainer<Apples>, and an Apple might not be implicitly convertible to a long, assuming it was passed to MyContainer<long>.Add<Apples>. Those of you who are used to C++ templates will recognize that the compiler will let you know if you're trying to perform an invalid conversion at compile time. However, generics don't have this compile-time luxury, so more restrictions are in place during compile time to disallow such a thing. Therefore, you must seek out a different solution, and a good one is to provide a conversion delegate to get the job done.

The base class library provides the System.Converter<TInput, TOutput> delegate specifically for this case. The syntax for this delegate might seem a bit foreign, but it's simply a generic delegate declaration, which I cover in detail in the section "Generic Delegates." When callers call Add<R>, they must also provide an instance of the generic Converter<T, R> delegate pointing to a method that knows how to convert from the source type to the target type. This explains the need for the IntToLongConverter method in the previous example. The Add<R> method then uses this delegate to do the actual conversion from one type to another. In this case, the conversion is an implicit one, but it still must be externalized this way because the compiler must accommodate the fact that the Add<R> method can have any type thrown at it at compile time.

Note

Staring with C# 4.0, there is a new syntax for marking an interface or delegate as covariant or contravariant. (I cover this new syntax in detail in the section "Co- and Contravariance" later in this chapter.) Depending on the situation, if your types T and R are implicitly convertible to one or the other, you might not have to implement a converter delegate as shown previously.

To facilitate enumeration of the container, I have also declared MyContainer<T> such that it implements IEnumerable<T>. This allows you to use the syntactically intuitive foreach construct. You'll notice some syntax that might look foreign to you if you're not familiar with C# iterators, specifically the yield statement.[41] However, notice how easy it is to create an enumerator for this class using the yield keyword. This was a welcome addition to the language because declaring and constructing objects that enumerate containers without it is a laborious task.

Generic Delegates

Quite often, generics are used in the context of container types, in which a closed type's field or internal array is based on the type argument given. Generic methods extend the capability of generic types by providing a finer granularity of generic scope. I have yet to discuss the power of generic delegates.

You're already familiar with the venerable delegate. If you were to declare a delegate that takes two parameters—the first being a long, and the second being an object—you could declare a delegate such as the following:

public delegate void MyDelegate( long l, object o );

In the previous section, you got a preview of a generic delegate when I showed the use of the generic converter delegate. The declaration for the generic converter delegate looks like this:

public delegate TOutput Converter<TInput, TOutput>(
    TInput input
);

It looks just like any other delegate, except that it has the telltale form of a generic with a type parameter list immediately following the name of the delegate. Just as nongeneric delegates look similar to method declarations without a body, generic delegate declarations look almost identical to generic method declarations without a body. The type parameter list follows the name of the delegate, but it precedes the parameter list of the delegate.

The generic converter uses the placeholder identifiers TInput and TOutput within its type parameter list, and those types are used elsewhere in the declaration for the delegate. In generic delegate declarations, the types in the type parameter list are in scope for the entire declaration of the delegate, including the return type as shown in the previous declaration for the generic converter delegate.

Creating an instance of the Converter<TInput, TOutput> delegate is the same as creating an instance of any other delegate. When you create an instance of the generic delegate, you can use the new operator, and you can explicitly provide the type list at compile time. Or, you might simply use the abbreviated syntax that I used in the MyContainer<T> example in the previous section, in which case the compiler deduces the type parameters. For convenience, I have reprinted the Main method of that example:

static void Main() {
        MyContainer<long> lContainer = new MyContainer<long>();
        MyContainer<int> iContainer = new MyContainer<int>();

        lContainer.Add( 1 );
        lContainer.Add( 2 );
        iContainer.Add( 3 );
        iContainer.Add( 4 );

        lContainer.Add( iContainer,
                        EntryPoint.IntToLongConverter );

        foreach( long l in lContainer ) {
            Console.WriteLine( l );
        }
    }

Notice that the second parameter to the last Add method is simply a reference to the method rather than an explicit creation of the delegate itself. This works because of the method group conversion rules defined by the C# language. When the actual delegate is created from the method, the closed type of the generic is inferred using a complex pattern-matching algorithm from the parameter types of the IntToLongConverter method itself. In fact, the call to Add<R> is devoid of any explicit type parameter list at the point of invocation. The compiler can do the exact same type of pattern matching to infer the closed form of the Add<R> method called, which in this case is Add<int>. You could just as well have written the code as follows, in which every type is provided explicitly:

static void Main() {
    MyContainer<long> lContainer = new MyContainer<long>();
    MyContainer<int> iContainer = new MyContainer<int>();

    lContainer.Add( 1 );
    lContainer.Add( 2 );
    iContainer.Add( 3 );
    iContainer.Add( 4 );

    lContainer.Add<int>( iContainer,
        new Converter<int, long>( EntryPoint.IntToLongConverter) );

    foreach( long l in lContainer ) {
        Console.WriteLine( l );
    }
}

In this example, all types are given explicitly, and the compiler is not left with the task of inferring them at compile time. Either way, the generated IL code is the same. Most of the time, you can rely on the type inference engine of the compiler. However, depending on the complexity of your code, you occasionally might find yourself needing to throw the compiler a bone by providing an explicit type list.

Along with providing a way to externalize type conversions from a container type, as in the previous examples, generic delegates help solve a specific problem that I demonstrate in the following code:

// THIS WON'T WORK AS EXPECTED!!!
using System;
using System.Collections.Generic;

public delegate void MyDelegate( int i );

public class DelegateContainer<T>
{
    public void Add( T del ) {
        imp.Add( del );
    }

    public void CallDelegates( int k ) {
        foreach( T del in imp ) {
//          del( k );
        }
    }

    private List<T> imp = new List<T>();
}

public class EntryPoint
{
    static void Main() {
        DelegateContainer<MyDelegate> delegates =
            new DelegateContainer<MyDelegate>();

        delegates.Add( EntryPoint.PrintInt );
    }

    static void PrintInt( int i ) {
        Console.WriteLine( i );
    }
}

As written, the previous code will compile. However, notice the commented line within the CallDelegates method. If you uncomment this line and attempt to recompile with the Microsoft compiler, you'll get the following error:

error CS0118: 'del' is a 'variable' but is used like a 'method'

The problem is that the compiler has no way of knowing that the type represented by the placeholder T is a delegate. Those of you who have been jumping ahead in this chapter might be wondering why there is no form of constraint (I cover constraints shortly) to give the compiler the hint that it is a delegate. Well, even if there were, the compiler could not possibly know how to call the delegate. The constraint would not carry the information about how many parameters the delegate accepts. Remember, unlike C++ templates, generics are dynamic, and closed types are formed at runtime rather than at compile time. So, at runtime, the delegate represented by del could take an arbitrary number of parameters. I can only imagine the headache caused by trying to devise a way to push a dynamic count of parameters onto the stack before calling the delegate. For all these reasons, it rarely makes sense to create a closed type from a generic, in which one of the type arguments is a delegate type because, after all, you cannot call through to it normally.

What you can do to help in this situation is apply a generic delegate to give the compiler a bit more information about what you want to do with this delegate. As in this example, using a generic delegate, you can effectively say, "I want you to use delegates that accept only one parameter of a generic type and return void." That's enough information to get the compiler past the block and allow it to generate code that makes sense for the generic. After all, if you give the compiler this amount of information, at least it knows how many parameters to push onto the stack before making the call through the delegate.

The following code shows how you could remedy the previous situation:

using System;
using System.Collections.Generic;

public delegate void MyDelegate<T>( T i );

public class DelegateContainer<T>
{
    public void Add( MyDelegate<T> del ) {
        imp.Add( del );
    }

    public void CallDelegates( T k ) {
        foreach( MyDelegate<T> del in imp ) {
            del( k );
        }
    }

    private List<MyDelegate<T> > imp = new List<MyDelegate<T> >();
}

public class EntryPoint
{
    static void Main() {
        DelegateContainer<int> delegates =
            new DelegateContainer<int>();

        delegates.Add( EntryPoint.PrintInt );
        delegates.CallDelegates( 42 );
    }

    static void PrintInt( int i ) {
        Console.WriteLine( i );
    }
}

Generic Type Conversion

As I covered earlier in this chapter, there is no implicit type conversion for different constructed types formed from the same generic type. The same rules that apply when determining if an object of type X is implicitly convertible to an object of type Y apply equally when determining whether an object of type List<int> is convertible to an object of type List<object>. When such conversion is desired, you must create a custom implicit conversion operator, just as in the case of converting objects of type X to objects of type Y when they share no inheritance relationship. Otherwise, you need to create a conversion method to go from one type to another.

For example, the following code is invalid:

// INVALID CODE!!!
public void SomeMethod( List<int> theList ) {
   List<object> theSameList = theList; // Ooops!!!
}

Note

This is not entirely true now because C# 4.0 introduced syntax to declare generic interfaces and delegates as variant. But for the sake of this section, everything is true as long as you treat all generic types as invariant, which is what you get by default if the generic declaration is not decorated with the variance syntax.

If you looked at the documentation of List<T>, you might have noticed a generic method named ConvertAll<TOutput>. Using this method, you can convert a generic list of type List<int> to List<object>. However, you must pass the method an instance of a conversion delegate as described in the previous section. So, to convert List<int> to List<object>, you must provide a delegate that knows how to convert an int instance into an object instance. By using the given delegate, it is the only way that ConvertAll<TOutput> can possibly know how to convert each contained instance from the source type to the destination type.

Those familiar with the Strategy pattern might find this a familiar notion. In essence, you can provide the ConvertAll<TOutput> method at runtime with a means of doing the conversion on the contained instances that, depending on the complexity of the conversion, can be tuned for the platform it is running on. In other words, if you were converting List<Apples> to List<Oranges>, you could provide a few different conversion methods to select from, depending on the circumstances. For example, maybe one of them is highly tuned for an environment with lots of resources, so it runs faster in those environments. Another version might be optimized for minimal resource usage but is much slower. At runtime, the proper conversion delegate is built to bind to the conversion method that is logical for the job at hand.

Default Value Expression

Sometimes when working with generic type definitions and generic method definitions, you need to initialize an object or a value instance of a parameterized type to its default value. Recall that the default value for a reference variable is the same as setting it to null, whereas the default value for a value type variable is equivalent to setting all its underlying bits to 0. You need an expression for generics to account for these two semantic differences; for that task, you can use the default value expression shown in the following code example:

using System;

public class MyContainer<T>
{
    public MyContainer() {
        // Create initial capacity.
        imp = new T[ 4 ];
for( int i = 0; i < imp.Length; ++i ) {
 imp[i] = default(T);
        }
    }

    public bool IsDefault( int i ) {
        if( i < 0 || i >= imp.Length ) {
            throw new ArgumentOutOfRangeException();
        }

        if( imp[i] == null ) {
            return true;
        } else {
            return false;
        }
    }

    private T[] imp;
}

public class EntryPoint
{
    static void Main() {
        MyContainer<int> intColl =
            new MyContainer<int>();

        MyContainer<object> objColl =
            new MyContainer<object>();

        Console.WriteLine( intColl.IsDefault(0) );
        Console.WriteLine( objColl.IsDefault(0) );
    }
}

Pay attention to the syntax within the MyContainer<T> constructor, in which each item in the array is initialized explicitly to its default value. At runtime, the type of T might be a value type or a reference type, so you cannot simply set the value to null and expect it to work for value types. In fact, if you attempt to assign imp[i] to null, the compiler will give you a friendly reminder with the following error:

error CS0403: Cannot convert null to type parameter 'T' because it could be 
Default Value Expression
a non-nullable value type. Consider using 'default(T)' instead.

You should also use the default expression when testing a variable for its default value because it has a different meaning depending on whether it is a value type or a reference type. However, in this case, the compiler cannot help you sniff out when you should do this, as you can see in the example. If you run the previous code, you get the output as follows:

False
True

This is not the intended result because they should both return true. If you modify the code so that the IsDefault method looks like the following example, you'll get output that is more in line with the intended result:

public bool IsDefault( int i ) {
    if( i < 0 || i >= imp.Length ) {
        throw new ArgumentOutOfRangeException();
    }

    if( Object.Equals(imp[i], default(T)) ) {
        return true;
    } else {
        return false;
    }
}

Nullable Types

Related to the previous discussion is the concept of null values and what semantic meaning they carry. The null state for reference types is easily representable. If the value of the reference is set to null, it typically means that the variable has no value. This is much different, semantically, than saying that the value is 0. Semantically, a variable set to null has no value, not even the value of 0. With respect to value types, it has traditionally been much more cumbersome to represent the semantic meaning of null. If you set the value to 0, it could mean that the value is null. Then what do you do to represent the case when the value is actually 0, not null? Many techniques involve maintaining another Boolean value to indicate that the value type actually conveys meaning, such as a bool value named isNull.

To help you avoid having to manage such a mundane, error-prone mechanism, the .NET base class library provides you with the System.Nullable<T> type, as demonstrated in the following code where I show two ways to use nullable types:

using System;

public class Employee
{
    public Employee( string firstName,
                     string lastName ) {
        this.firstName = firstName;
        this.lastName = lastName;

        this.terminationDate = null;
        this.ssn = default(Nullable<long>);
    }

    public string firstName;
    public string lastName;

    public Nullable<DateTime> terminationDate;
    public long? ssn;   // Shorthand notation
}

public class EntryPoint
{
    static void Main() {
        Employee emp = new Employee( "Vasya",
                                     "Pupkin" );
        emp.ssn = 1234567890;

        Console.WriteLine( "{0} {1}",
                           emp.firstName,
                           emp.lastName );
        if( emp.terminationDate.HasValue ) {
            Console.WriteLine( "Start Date: {0}",
                               emp.terminationDate );
        }

        long tempSSN = emp.ssn ?? −1;
        Console.WriteLine( "SSN: {0}",
                           tempSSN );
    }
}

This code demonstrates two ways to declare a nullable type. The first nullable field within type Employee is the terminationDate field, which is declared using the System.Nullable<DateTime> type. The second nullable value within Employee is the ssn field; however, this time I chose to use a C# shorthand notation for nullable types, in which you simply follow the field's type declaration with a question mark. Internally, the compiler handles this in exactly the same way as with the declaration for the terminationDate field.

Tip

Personally, I feel that even though using Nullable<T> explicitly requires more typing, it's definitely a lot harder to overlook than the little question mark at the end of the field type when you're reading code. Always prefer to write clearly readable code rather than trite cute code. No offense to the question mark syntax! It's just my preference.

One last thing to consider when using nullable types is how you assign to and from them. In the constructor for Employee, you can see that I assign null to the nullable types at first. The compiler uses an implicit conversion for the null value to do the right thing. In fact, when I assign the ssn field in the constructor, I use the default expression syntax, which is the same thing the compiler does when I assign the terminationDate nullable value to null.

One of the properties of Nullable<T> is HasValue, which returns true when the nullable value is non-null and returns false otherwise. Finally, you must consider what it means to assign a nullable type to a non-nullable type. For example, in the Main method, I want to assign tempSSN based upon the value of emp.ssn. However, because emp.ssn is nullable, what should tempSSN be assigned to if emp.ssn happens to have no value? This is when you must use the null coalescing operator ??. This operator allows you to designate what you want the non-nullable value to be set to in the event that the nullable value you're assigning from has no value. So, in the previous example, I'm saying, "Set the value of tempSSN to emp.ssn, and if emp.ssn has no value, set tempSSN to −1 instead." Armed with these tools, it's a snap to represent values within a system that might be semantically null, which is handy when you're using values to represent fields within a database field that is nullable.

Constructed Types Control Accessibility

When you build constructed types from generic types, you must consider the accessibility of both the generic type and the types provided as the type arguments, in order to determine the accessibility of the whole constructed type.

For example, the following code is invalid and will not compile:

public class Outer
{
    private class Nested
    {
    }

    public class GenericNested<T>
    {
    }

    private GenericNested<Nested> field1;
    public GenericNested<Nested> field2; // Ooops!
}

The problem is with field2. The Nested type is private, so how can GenericNested<Nested> possibly be public? Of course, the answer is that it cannot. With constructed types, the accessibility is an intersection of the accessibility of the generic type and the types provided in the argument list.

Generics and Inheritance

C# generic types cannot directly derive from a type parameter. However, you can use the following type parameters to construct the base types they do derive from:

// This is invalid!!
public class MyClass<T> : T
{
}

// But this is valid.
public class MyClass<T> : Stack<T>
{
}

Tip

With C++ templates, deriving directly from a type parameter provides a special flexibility. If you've ever used the Active Template Library (ATL) to do COM development, you have no doubt come across this technique because ATL employs it extensively to avoid the need for virtual method calls. The same technique is used with C++ templates to generate entire hierarchies at compile time. For more examples, I suggest you read Andrei Alexandrescu's Modern C++ Design: Generic Programming and Design Patterns Applied (Boston, MA: Addison-Wesley Professional, 2001). This is yet another example showing how C++ templates are static in nature, whereas C# generics are dynamic.

Let's examine techniques that you can use to emulate the same behavior to some degree. As is often the case, you can add one more level of indirection to achieve something similar. In C++, when a template type derives directly from one of the type arguments, it is often assumed that the type specified for the type argument exhibits a certain desired behavior.

For example, you can do the following using C++ templates:

// NOTE: This is C++ code used for the sake of example

class Employee
{
    public:
        long get_salary() {
            return salary;
        }
        void set_salary( long salary ) {
            this->salary = salary;
        }

    private:
        long salary;
};

template< class T >
class MyClass : public T
{
};

void main()
{
    MyClass<Employee> myInstance;
    myInstance.get_salary();
}

In the main function, pay attention to the call to get_salary. Even though it looks odd at first, it works just fine because MyClass<T> inherits the implementation of whatever type is specified for T at compile time. In this case, that type, Employee, implements get_salary, and MyClass<Employee> inherits that implementation. Clearly, an assumption is being placed on the type that is provided for T in MyClass<T> that the type will support a method named get_salary. If it does not, the C++ compiler will complain at compile time. This is a form of static polymorphism or policy-based programming. In traditional cases, polymorphism is explained within the context of virtual methods known as dynamic polymorphism. You cannot implement static polymorphism with C# generics. However, you can require that the type arguments given when forming a closed type support a specific contract by using a mechanism called constraints, which I cover in the following section.

Constraints

So far, the majority of generics examples that I've shown involve some sort of collection-style class that holds a bunch of objects or values of a specific type. But you'll often need to create generic types that not only contain instances of various types but also use those objects directly by calling methods or accessing properties on them. For example, suppose that you have a generic type that holds instances of arbitrary geometric shapes that all implement a property named Area. Also, you need the generic type to implement a property—say, TotalArea—in which all the areas of the contained shapes are accumulated. The guarantee here is that each geometric shape in the generic container will implement the Area property. You might be inclined to write code like the following:

using System;
using System.Collections.Generic;

public interface IShape
{
    double Area {
        get;
    }
}

public class Circle : IShape
{
    public Circle( double radius ) {
        this.radius = radius;
    }

    public double Area {
        get {
            return 3.1415*radius*radius;
        }
    }

    private double radius;
}

public class Rect : IShape
{
    public Rect( double width, double height ) {
        this.width = width;
        this.height = height;
    }

    public double Area {
        get {
            return width*height;
        }
    }

    private double width;
    private double height;
}

public class Shapes<T>
{
    public double TotalArea {
        get {
            double acc = 0;
            foreach( T shape in shapes ) {
                // THIS WON'T COMPILE!!!
                acc += shape.Area;
            }
            return acc;
        }
    }

    public void Add( T shape ) {
        shapes.Add( shape );
    }

    private List<T> shapes = new List<T>();
}

public class EntryPoint
{
    static void Main() {
        Shapes<IShape> shapes = new Shapes<IShape>();

        shapes.Add( new Circle(2) );
        shapes.Add( new Rect(3, 5) );

        Console.WriteLine( "Total Area: {0}",
                           shapes.TotalArea );
    }
}

There is one major problem, as the code won't compile. The offending line of code is inside the TotalArea property of Shapes<T>. The compiler complains with the following error:

error CS0117: 'T' does not contain a definition for 'Area'

All this talk of requiring the contained type T to support the Area property sounds a lot like a contract because it is! C# generics are dynamic as opposed to static in nature, so you cannot achieve the desired effect without some extra information. Whenever you hear the word contract within the C# world, you might start thinking about interfaces. Therefore, I chose to have both of my shapes implement the IShape interface. Thus, the IShape interface defines the contract, and the shapes implement that contract. However, that still is not enough for the C# compiler to be able to compile the previous code.

C# generics must have a way to enforce the rule that the type T supports a specific contract at runtime. A naïve attempt to solve the problem could look like the following:

public class Shapes<T>
{
public double TotalArea {
        get {
            double acc = 0;
            foreach( T shape in shapes ) {
                // DON'T DO THIS!!!
                IShape theShape = (IShape) shape;
                acc += theShape.Area;
            }
            return acc;
        }
    }

    public void Add( T shape ) {
        shapes.Add( shape );
    }

    private List<T> shapes = new List<T>();
}

This modification to Shapes<T> indeed does compile and work most of the time. However, this generic has lost some of its innocence due to the type cast within the foreach loop. Just imagine that if during a late-night caffeine-induced trance, you attempted to create a constructed type Shapes<int>. The compiler would happily oblige. But what would happen if you tried to get the TotalArea property from a Shapes<int> instance? As expected, you would be treated to a runtime exception as the TotalArea property accessor attempted to cast an int into an IShape. One of the primary benefits of using generics is better type safety, but in this example I tossed type safety right out the window. So, what are you supposed to do? The answer lies in a concept called generic constraints. Check out the following correct implementation:

public class Shapes<T>
    where T: IShape
{
    public double TotalArea {
        get {
            double acc = 0;
            foreach( T shape in shapes ) {
                acc += shape.Area;
            }
            return acc;
        }
    }

    public void Add( T shape ) {
        shapes.Add( shape );
    }

    private List<T> shapes = new List<T>();
}

Notice the extra line under the first line of the class declaration using the where keyword. This says, "Define class Shapes<T> where T must implement IShape." Now the compiler has everything it needs to enforce type safety, and the JIT compiler has everything it needs to build working code at runtime. The compiler has been given a hint to help it notify you, with a compile-time error, when you attempt to create constructed types where T does not implement IShape.

The syntax for constraints is pretty simple. There can be one where clause for each type parameter. Any number of constraints can be listed following the type parameter in the where clause. However, only one constraint can name a class type (because the CLR has no concept of multiple inheritance), so that constraint is known as the primary constraint. Additionally, instead of specifying a class name, the primary constraint can list the special words class or struct, which are used to indicate that the type parameter must be any class or any struct. The constraint clause can then include as many secondary constraints as possible, such as a list of interfaces that the parameterized type must implement. Finally, you can list a constructor constraint that takes the form new() at the end of the constraint list. This constrains the parameterized type so it is required to have a default parameterless constructor. Class types must have an explicitly defined default constructor to satisfy this constraint, whereas value types have a system-generated default constructor.

It is customary to list each where clause on a separate line in any order under the class header. A comma separates each constraint following the colon in the where clause. That said, let's take a look at some constraint examples:

using System.Collections.Generic;

public class MyValueList<T>
    where T: struct
// But can't do the following
//  where T: struct, new()
{
    public void Add( T v ) {
        imp.Add( v );
    }

    private List<T> imp = new List<T>();
}

public class EntryPoint
{
    static void Main() {
        MyValueList<int> intList =
            new MyValueList<int>();

        intList.Add( 123 );

        // CAN'T DO THIS.
        // MyValueList<object> objList =
        //  new MyValueList<object>();
    }
}

In this code, you can see an example of the struct constraint in the declaration for a container that can contain only value types. The constraint prevents one from declaring the objList variable that I have commented out in this example because the result of attempting to compile it presents the following error:

error CS0453: The type 'object' must be a non-nullable value type in order 
Constraints
to use it as parameter 'T' in the generic type or method 'MyValueList<T>'

Alternatively, the constraint could have also claimed to allow only class types. Incidentally, in the Visual Studio version of the C# compiler, I can't create a constraint that includes both class and struct. Of course, doing so is pointless because the same effect comes from including neither struct nor class in the constraints list. Nevertheless, the compiler complains with an error if you try to do so, claiming the following:

error CS0449: The 'class' or 'struct' constraint must come before any 
Constraints
other constraints

This looks like the compiler error could be better stated by saying that only one primary constraint is allowed in a constraint clause. You'll also see that I commented out an alternate constraint line, in which I attempted to include the new() constraint to force the type given for T to support a default constructor. Clearly, for value types, this constraint is redundant and should be harmless to specify. Even so, the compiler won't allow you to provide the new() constraint together with the struct constraint. Now let's look at a slightly more complex example that shows two constraint clauses:

using System;
using System.Collections.Generic;

public interface IValue
{
    // IValue methods.
}

public class MyDictionary<TKey, TValue>
    where TKey: struct, IComparable<TKey>
    where TValue: IValue, new()
{
    public void Add( TKey key, TValue val ) {
        imp.Add( key, val );
    }

    private Dictionary<TKey, TValue> imp
        = new Dictionary<TKey, TValue>();
}

I declared MyDictionary<TKey, TValue> so that the key value is constrained to value types. I also want those key values to be comparable, so I've required the TKey type to implement IComparable<TKey>. This example shows two constraint clauses, one for each type parameter. In this case, I'm allowing the TValue type to be either a struct or a class, but I do require that it support the defined IValue interface as well as a default constructor.

Overall, the constraint mechanism built into C# generics is simple and straightforward. The complexity of constraints is easy to manage and decipher with few if any surprises. As the language and the CLR evolve, I suspect that this area will see some additions as more and more applications for generics are explored. For example, the ability to use the class and struct constraints within a constraint clause was a relatively late addition to the standard.

Finally, the format for constraints on generic interfaces is identical to that of generic classes and structs.

Constraints on Nonclass Types

So far, I've discussed constraints within the context of classes, structs, and interfaces. In reality, any entity that you can declare generically is capable of having an optional constraints clause. For generic method and delegate declarations, the constraints clauses follow the formal parameter list to the method or delegate. Using constraint clauses with method and delegate declarations does provide for some odd-looking syntax, as shown in the following example:

using System;

public delegate R Operation<T1, T2, R>( T1 val1,
                                        T2 val2 )
    where T1: struct
    where T2: struct
    where R:  struct;

public class EntryPoint
{
    public static double Add( int val1, float val2 ) {
        return val1 + val2;
    }

    static void Main() {
        var op =
            new Operation<int, float, double>( EntryPoint.Add );

        Console.WriteLine( "{0} + {1} = {2}",
                           1, 3.2, op(1, 3.2f) );
    }
}

I declared a generic delegate for an operator method that accepts two parameters and has a return value. My constraint is that the parameters and the return value all must be value types. Similarly, for generic methods, the constraints clauses follow the method declaration but precede the method body.

Co- and Contravariance

Variance is all about convertibility and being able to do what makes type-sense. For example, consider the following code, which demonstrates array covariance that has been possible in C# since the 1.0 days:

using System;

static class EntryPoint
{
    static void Main() {
        string[] strings = new string[] {
            "One",
"Two",
            "Three"
        };

        DisplayStrings( strings );

// Array covariance rules allow the following
// assignment
object[] objects = strings;

        // But what happens now?
        objects[1] = new object();
        DisplayStrings( strings );
    }

    static void DisplayStrings( string[] strings ) {
        Console.WriteLine( "----- Printing strings -----" );
        foreach( var s in strings ) {
            Console.WriteLine( s );
        }
    }
}

At the beginning of the Main method, I create an array of strings and then immediately pass it to DisplayStrings to print them to the console. Then, I assign a variable of type objects[] from the variable strings. After all, because strings and objects are reference type variables, at first glance it makes logical sense to be able to assign strings to objects because a string is implicitly convertible to an object. However, notice right after doing so, I modify slot one and replace it with an object instance. What happens when I call DisplayStrings the second time passing the strings array? As you might expect, the runtime throws an exception of type ArrayTypeMismatchException shown as follows:

Unhandled Exception: System.ArrayTypeMismatchException: Attempted to access an
element as a type incompatible with the array.

Array covariance in C# has been in the language since the beginning for Java compatibility. But because it is flawed, and some say broken, then how can we fix this problem? There are a few ways indeed. Those of you familiar with functional programming will naturally suggest invariance as the solution. That is, if an array is invariant similar to System.String, a copy is made typically in a lazy fashion at the point where one is assigned into another variable. However, let's see how we might fix this problem using generics:

using System;
using System.Collections.Generic;

static class EntryPoint
{
    static void Main() {
        List<string> strings = new List<string> {
            "One",
            "Two",
            "Three"
};

        // THIS WILL NOT COMPILE!!!
        List<object> objects = strings;
    }
}

The spirit of the preceding code is identical to the array covariance example, but it will not compile. If you attempt to compile this, you will get the following compiler error:

error CS0029: Cannot implicitly convert type 
Co- and Contravariance
'System.Collections.Generic.List<string>' to 
Co- and Contravariance
'System.Collections.Generic.List<object>'

The ultimate problem is that each constructed type is an individual type, and even though they might originate from the same generic type, they have no implicit type relation between them. For example, there is no implicit relationship between List<string> and List<object>, and just because they both are constructed types of List<T> and string is implicitly convertible to object does not imply that they are convertible from one to the other.

Don't lose hope, though. There is a syntax added in C# 4.0 that allows you to achieve the desired result. Using this new syntax, you can notate a generic interface or delegate indicating whether it supports covariance or contravariance. Additionally, the new variance rules apply only to constructed types in which reference types are passed for the type arguments to the generic type.

Covariance

Within strongly typed programming languages such as C#, an operation is covariant if it reflects and preserves the ordering of types so they are ordered from more specific types to more generic types. To illustrate, I'll borrow from the example in the previous section to show how array assignment rules in C# are covariant:

string s = "Hello";
object o = s;

string[] strings = new string[3];
object[] objects = strings;

The first two lines make perfect sense; after all, variables of type string are implicitly convertible to type object because string derives from object. The second set of lines shows that variables of type string[] are implicitly convertible to variables of type object[]. And because the ordering of types between the two implicit assignments is identical that is, from a more specialized type (string) to a more generic type (object) the array assignment operation is said to be covariant.

Now, to translate this concept to generic interface assignment, an interface of type IOperation<T> is covariance-convertible to IOperation<R> if there exists an implicit reference conversion from T to R and IOperation<T> to IOperation<R>. Simply put, if for the two conversion operations just mentioned, T and R are on the same sides of the conversion operations, the conversion operation is covariant. For example, let the arrow shown following represent the operation. And because T and R appear on the same sides of the operation in both cases, the operation is covariant in nature.

T
Covariance
R
IOperation<T>
Covariance
IOperation<R>

Note

C# variance rules do not apply to value types; that is, types that are not reference convertible. In other words, IOperation<int> is not covariance-convertible to IOperation<double>, even though int is implicitly convertible to double.

Let's consider an example of a custom collection called MyCollection<T> that implements the interface IMyCollection<T>:

using System;
using System.Collections.Generic;

interface IMyCollection<T>
{
    void AddItem( T item );
    T GetItem( int index );
}

class MyCollection<T> : IMyCollection<T>
{
    public void AddItem( T item ) {
        collection.Add( item );
    }

    public T GetItem( int index ) {
        return collection[index];
    }

    private List<T> collection = new List<T>();
}

static class EntryPoint
{
    static void Main() {
        var strings = new MyCollection<string>();
        strings.AddItem( "One" );
        strings.AddItem( "Two" );

        IMyCollection<string> collStrings = strings;
        PrintCollection( collStrings, 2 );
    }

    static void PrintCollection( IMyCollection<string> coll,
int count ) {
        for( int i = 0; i < count; ++i ) {
            Console.WriteLine( coll.GetItem(i) );
        }
    }
}

Of course, the collection MyCollection<T> is extremely contrived and we would never author a real collection type like this, but I have written it this way to keep the example brief and to focus on covariance. The preceding code compiles and runs just fine while printing out the two strings in the MyCollection<string> instance to the console. But now, let's imagine that we want PrintCollection to accept an instance of type IMyCollection<object> rather than IMyCollection<string>. After all, it is logical that a collection of strings is a collection of objects as well. If you simply just change the signature of PrintCollection to accept IMyCollection<object>, you will get a compiler error at the point of invocation. That's because what is logical to you and me is not necessarily logical to the compiler because, by default, constructed generic types are invariant and there is no implicit conversion from one to the other. Something else is needed. Check out the following modification that compiles and works as expected. I have bolded the differences to pay attention to:

using System;
using System.Collections.Generic;

interface IMyCollection<T>
{
    void AddItem( T item );
}

interface IMyEnumerator<out T>
{
    T GetItem( int index );
}

class MyCollection<T> : IMyCollection<T>,
                        IMyEnumerator<T>
{
    public void AddItem( T item ) {
        collection.Add( item );
    }

    public T GetItem( int index ) {
        return collection[index];
    }

    private List<T> collection = new List<T>();
}

static class EntryPoint
{
    static void Main() {
        var strings = new MyCollection<string>();
        strings.AddItem( "One" );
        strings.AddItem( "Two" );
IMyEnumerator<string> collStrings = strings;

        // Covariance in action!
       IMyEnumerator<object> collObjects = collStrings;

        PrintCollection( collObjects, 2 );
    }

    static void PrintCollection( IMyEnumerator<object> coll,
                                 int count ) {
        for( int i = 0; i < count; ++i ) {
            Console.WriteLine( coll.GetItem(i) );
        }
    }
}

First, notice that I split the previous implementation of IMyCollection into two interfaces named IMyCollection and IMyEnumerator. I'll explain why in a moment. Also, notice that PrintCollection accepts a variable of type IMyEnumerator<object> rather than IMyCollection<string>. But most importantly, look very closely at the IMyEnumerator<T> declaration and pay attention to the way the generic parameter is decorated with the out keyword.

The out keyword in the generic parameter list is how you denote that a generic interface is covariant in T. In other words, it's how you tell the compiler that if R is implicitly convertible to S, IMyEnumerator<R> is implicitly convertible to IMyEnumerator<S>. Why is the keyword named out? Because it just so happens that generic interfaces that are covariant in T typically have T in an output position of the methods within. Now you can see why I had to split the original IMyCollection interface into two interfaces because the IMyCollection.AddItem method does not have T in the output position.

Note

The keywords in and out were likely chosen by the compiler team because, as shown previously, covariant interfaces have the variant type in the output position and vice versa for contravariance. However, I will show in a later section that this oversimplified view becomes rather confusing when higher-order functions (or functionals) via delegates are involved.

The venerable IEnumerable<T> and IEnumerator<T> types are denoted as covariant with the out keyword starting with the release of C# 4.0. This is a tremendous help, especially when using LINQ.

Contravariance

As you might expect, contravariance is the opposite of covariance. That is, for generic interface assignment, an interface of type IOperation<T> is contravariance-convertible to IOperation<R> if there exists an implicit reference conversion from R to T and IOperation<T> to IOperation<R>. Simply put, if T and R are on opposite sides of the conversion operation for both conversions, the conversion operation is contravariant. For example, let the following arrow represent the operation. And because T and R appear on opposite sides of the operation in both cases, the operation is contravariant in nature.

R
Contravariance
T
IOperation<T>
Contravariance
IOperation<R>

Contravariant generic parameters in generic interfaces and delegates are notated using the new in generic parameter decoration. To illustrate, let's revisit the contrived MyCollection<T> class in the previous section and imagine that we want the ability to remove items from the collection (the areas of interest are in bold):

using System;
using System.Collections.Generic;

class A { }
class B : A { }

interface IMyCollection<T>
{
    void AddItem( T item );
}

interface IMyTrimmableCollection<in T>
{
    void RemoveItem( T item );
}

class MyCollection<T> : IMyCollection<T>,
                        IMyTrimmableCollection<T>
{
    public void AddItem( T item ) {
        collection.Add( item );
    }

    public void RemoveItem( T item ) {
        collection.Remove( item );
    }

    private List<T> collection = new List<T>();
}

static class EntryPoint
{
    static void Main() {
        var items = new MyCollection<A>();
        items.AddItem( new A() );

        B b = new B();
        items.AddItem( b );

        IMyTrimmableCollection<A> collItems = items;

        // Contravariance in action!
        IMyTrimmableCollection<B> trimColl = collItems;
        trimColl.RemoveItem( b );
}
}

I have trimmed some of the code from the covariance example in order to focus squarely on the contravariance case. Notice the use of the in keyword in the declaration for the IMyTrimmableCollection<T> interface. This tells the compiler that with respect to the desired operation in this example (trimming in this case), there exists an implicit contravariance-conversion from IMyTrimmableCollection<A> to IMyTrimmableCollection<B> because there is an implicit conversion from B to A. At first glance, the conversion and the assignment of collItems into the trimColl might feel foreign. But if for MyCollection<A> I can invoke RemoveItem passing an A instance, I should be able to invoke RemoveItem passing a B instance because B is an A based on the inheritance rules.

Up to this point, I have shown examples of both covariance and contravariance using modifications to the same contrived collection class. You have seen how enumeration on the collection is covariant and how removal from the collection is contravariant. What about addition to the collection? Which flavor of variance is it? We already have the IMyCollection<T> interface, which is repeated here for convenience:

interface IMyCollection<T>
{
    void AddItem( T item );
}

If you have an IMyCollection<A> reference, you should be able to add instances of B if B derives from A. So calling AddItem on IMyCollection<A> passing a B instance should be equivalent to calling IMyCollection<B> passing a B instance. Therefore, the operation of adding an instance to the collection is contravariant based on the definition. That is, if B is convertible to A and IMyCollection<B> is convertible to IMyCollection<A>, the operation is contravariant.

Now that you have discovered that the operation of adding an item to the collection is contravariant, you should decorate our interface accordingly:

interface IMyCollection<in T>
{
    void AddItem( T item );
}

Invariance

A generic interface or delegate type in which the generic parameters are not decorated at all is invariant. Naturally, all such interfaces and delegates were invariant prior to C# 4.0 because the in and out decorations to generic parameters did not exist before then. Remember from an earlier section, the contrived IMyCollection<T> interface looked like the following:

interface IMyCollection<T>
{
    void AddItem( T item );
    T GetItem( int index );
}

If we must keep these two methods in the same interface, we have no choice but to leave the interface as invariant. If the compiler were to allow us to decorate the generic parameter T with the out keyword, then we would be in the same broken boat that the array covariance is in. That is, we would be allowed to compile code that would appear to allow us to add instances of incompatible types to a collection. Why is that? Well, let's imagine for a moment that we could mark the preceding interface as covariant:

// This won't work!
interface IMyCollection<out T>
{
    void AddItem( T item );
    T GetItem( int index );
}

Then, based on the definition of covariance, a variable of type IMyCollection<string> would be assignable to a variable of type IMyCollection<object>. And then, through the latter variable, we would be able to do something like the following:

// Nothing but pure evil!
MyCollection<string> strings = ...;
IMyCollection<object> objects = strings;
objects.AddItem( new MonkeyWrench() );

Therefore, much of the pain associated with array invariance in C# is avoided by using generics coupled with the variance syntax added to the language in C# 4.0. In other words, the variance rules for generics are type safe whereas the variance rules for plain old arrays are not.

Variance and Delegates

In general, generic delegates follow the same rules as generic interfaces when applying variance decorations to generic parameters. The .NET Base Class Library (BCL) contains handy generic delegate types such as Action<> and Func<>, which are applicable in many instances saving you from having to define your own custom delegate types. The Action<> delegates can be used to hold methods that accept up to 16 parameters and have no return value, and the Func<> delegates can be used to hold methods that accept up to 16 parameters and do return a value.

Note

Prior to the .NET 4.0 BCL, the Action<> and Func<> delegates only accepted up to four parameters. Currently, they support up to 16.

Starting with .NET 4.0, these generic delegates have also been marked appropriately for variance. Thus, the two parameter versions of these will look like the following:

public delegate void Action< in T1, in T2 >( T1 arg1, T2 arg2 );
public delegate TResult Func< in T1, in T2, out TResult>( T1 arg1, T2 arg2 );

Now for an example of delegate variance, let's consider a type hierarchy:

class Animal
{
}
class Dog : Animal
{
}

Suppose that you had a couple of methods like the following defined in some class:

static void SomeFuntion( Animal animal );
static void AnotherFunction( Dog dog );

Then because the function signature matches the delegate signature, it makes sense that you could assign SomeFunction to an instance of Action<Animal> like the following:

Action<Animal> action1 = SomeFunction;

When one invokes action1, one can pass a Dog or an Animal because Dog is implicitly convertible to Animal. Let's suppose that you later create an Action<Dog> instance such as the following:

Action<Dog> action2 = AnotherFunction;

When one invokes action2, one can pass a Dog instance. But also notice that because one can also pass a Dog instance to SomeFunction, it would have been possible to create action2 as shown here:

Action<Dog> action2 = SomeFunction;

This type of variance-assignment (contravariance in this case) from method group to delegate instance has been supported in C# for quite some time. So, if the preceding is possible, it makes sense to be able to do the following, which one can do starting in C# 4.0:

Action<Dog> action2 = action1;

Now, let's see a short example of contravariance-assignment with Action<T> at work using the same object hierarchy shown in the previous example:

using System;

class Animal
{
    public virtual void ShowAffection() {
        Console.WriteLine( "Response unknown" );
    }
}

class Dog : Animal
{
    public override void ShowAffection() {
        Console.WriteLine( "Wag Tail..." );
    }
}

static class EntryPoint
{
    static void Main() {
        Action<Animal> petAnimal = (Animal a) => {
            Console.Write( "Petting animal and response is: " );
            a.ShowAffection();
};

        // Contravariance rule in action!
        //
        // Since Dog -> Animal and
        // Action<Animal> -> Action<Dog>
        // then the following assignment is contravariant
        Action<Dog> petDog = petAnimal;

        petDog( new Dog() );
    }
}

In the Main method, I have created an instance of Action<Animal> that holds a reference to a function that accepts an Animal instance and calls the ShowAffection method on the instance.

Note

I use the lambda syntax to assign a function to the Action<Animal> instance for brevity. If you are unfamiliar with this syntax and you are itching to learn more, you can jump to Chapter 15 soon to read all about it.

The next line of code in Main is where the fun begins. This is where I assign the instance of Action<Animal> into a reference to Action<Dog>. And because Dog is implicitly convertible to Animal, yet Action<Animal> is implicitly convertible to Action<Dog>, the assignment is contravariant. If at this point you are struggling to get your head wrapped around how Action<Animal> is implicitly convertible to Action<Dog> when Animal is not implicitly convertible to Dog, try to keep in mind that the action is the focal point. If an action can operate on Animal instances, it can certainly operate on Dog instances.

But now let's kick it up a notch! In functional programming disciplines, it is common to pass actual functions as parameters to other functions. This has always been easy in C# using delegates (and in Chapter 15, you'll see that it's even easier using lambda expressions). Functions that accept functions as parameters are often called higher-level functions or functionals. So what sort of variance is involved when assigning compatible instances of higher-order functions to each other? Let's investigate by introducing a new delegate definition that looks like the following:

delegate void Task<T>( Action<T> action );

Here we have defined a delegate, Task<T>, which will reference a function that accepts another delegate of type Action<T>.

Note

Please don't confuse the Task type in this example with the Task type in the Task Parallel Library (TPL).

If we were to mark this delegate as variant, would we notate the type parameter with in or out? Let's investigate by looking at the following example:

static class EntryPoint
{
static void Main() {
        Action<Animal> petAnimal = (Animal a) => {
            Console.Write( "Petting animal and response is: " );
            a.ShowAffection();
        };

        // Contravariance rule in action!
        //
        // Since Dog -> Animal and
        // Action<Animal> -> Action<Dog>
        // then the following assignment is contravariant
        Action<Dog> petDog = petAnimal;

        petDog( new Dog() );

        Task<Dog> doStuffToADog = BuildTask<Dog>();
        doStuffToADog( petDog );

        // But a task that accepts an action to a dog can also
        // accept an action to an animal
        doStuffToADog( petAnimal );

        // Therefore, it is logical for Task<Dog> to be implicitly
        // convertible to Task<Animal>
        //
        // Covariance in action!
        //
        // Since Dog -> Animal and
        // Task<Dog> -> Task<Animal>
        // then the following assignment is covariant
        Task<Animal> doStuffToAnAnimal = doStuffToADog;
        doStuffToAnAnimal( petAnimal );
        doStuffToADog( petAnimal );
    }

    static Task<T> BuildTask<T>() where T : new() {
        return (Action<T> action) => action( new T() );
    }
}

First, notice that I created a BuildTask<T> generic helper method to make my code a little more readable. In Main, I create an instance of Task<Dog> and assign it to the doStuffToADog variable. doStuffToADog holds a reference to a delegate that accepts an Action<Dog> instance as a parameter. I then invoke doStuffToADog passing petDog, which is an instance of Action<Dog>. But in the previous example we discovered that Action<Animal> is implicitly convertible to Action<Dog>, so that's how I can get away with passing petAnimal in the second invocation of doStuffToADog.

Now let's follow the same thought pattern as the previous example, in which you discovered that Action<Animal> is contravariance-assignable to an Action<Dog>. In Main, I create an instance of Task<Animal> and assign it to the doStuffToAnAnimal variable. When I invoke doStuffToAnAnimal, I can certainly pass an instance of Action<Animal>. But because Action<Animal> can also be passed to Task<Dog> at invocation time, it implies that an instance of Task<Dog> can be assigned to an instance of Task<Animal>. Indeed, that is what I am demonstrating in this example. But is it contravariance or covariance?

At first glance, because T is used on the right side in the declaration of the Task<T> delegate, one might be inclined to say that we must decorate the type parameter T with the in keyword. However, let's analyze the situation. Because Dog is implicitly convertible to Animal, and Task<Dog> is implicitly convertible to Task<Animal>, the assignment is covariant because the direction of conversion with respect to T is the same direction in both operations. Therefore, the type parameter must be decorated with the out keyword, thus making the declaration for Task<T> look like the following:

delegate void Task<out T>( Action<T> action );

The point to understand here is that you cannot choose the in or out keyword based solely on which side of the delegate declaration the generic parameter is used. You must analyze the conversion to determine whether it is covariant or contravariant, and then make your choice accordingly. Of course, if you choose the wrong one, the compiler will certainly let you know about it.

Generic System Collections

It seems that the most natural use of generics within C# and the CLR is for collection types. Maybe that's because you can gain a huge amount of efficiency when using generic containers to hold value types when compared with the collection types within the System.Collections namespace. Of course, you cannot overlook the added type safety that comes with using the generic collections. Any time you get added type safety, you're guaranteed to reduce runtime type conversion exceptions because the compiler can catch many of them at compile time.

I encourage you to look at the .NET Framework documentation for the System.Collections.Generic namespace. There you will find all the generic collection classes made available by the Framework. Included in the namespace are Dictionary<TKey, TValue>, LinkedList<T>, List<T>, Queue<T>, SortedDictionary<TKey, TValue>, SortedList<T>, HashSet<T>, and Stack<T>.

Based on their names, the uses of these types should feel familiar compared to the nongeneric classes under System.Collections. Although the containers within the System.Collections.Generic namespace might not seem complete for your needs, you have the possibility to create your own collections, especially given the extendable types in System.Collections.ObjectModel.

When creating your own collection types, you'll often find the need to be able to compare the contained objects. When coding in C#, it feels natural to use the built-in equality and inequality operators to perform the comparison. However, I suggest that you stay away from them because the support of operators by classes and structs—although possible—is not part of the CLS. Some languages have been slow to pick up support for operators. Therefore, your container must be prepared for the case when it contains types that don't support operators for comparison. This is one of the reasons why interfaces such as IComparer and IComparable exist.

When you create an instance of the SortedList type within System.Collections, you have the opportunity to provide an instance of an object that supports IComparer. The SortedList then utilizes that object when it needs to compare two key instances that it contains. If you don't provide an object that supports IComparer, the SortedList looks for an IComparable interface on the contained key objects to do the comparison. Naturally, you'll need to provide an explicit comparer if the contained key objects don't support IComparable. The overloaded versions of the constructor that accept an IComparer type exist specifically for that case.

The generic version of the sorted list, SortedList<TKey, TValue>, follows the same sort of pattern. When you create a SortedList<TKey, TValue>, you have the option of providing an object that implements the IComparer<T> interface so it can compare two keys. If you don't provide one, the SortedList<TKey, TValue> defaults to using what's called the generic comparer. The generic comparer is simply an object that derives from the abstract Comparer<T> class and can be obtained through the static property Comparer<T>.Default. Based upon the nongeneric SortedList, you might think that if the creator of SortedList<TKey, TValue> did not provide a comparer, it would just look for IComparable<T> on the contained key type. This approach would cause problems because the contained key type could either support IComparable<T> or the nongeneric IComparable. Therefore, the default comparer acts as an extra level of indirection. The default comparer checks to see whether the type provided in the type parameter implements IComparable<T>. If it does not, looks to see whether it supports IComparable, thus using the first one that it finds. Using this extra level of indirection provides greater flexibility with regard to the contained types. Let's look at an example to illustrate what I've just described:

using System;
using System.Collections.Generic;

public class EntryPoint
{
    static void Main() {
        SortedList<int, string> list1 =
            new SortedList<int, string>();

        SortedList<int, string> list2 =
            new SortedList<int, string>( Comparer<int>.Default );

        list1.Add( 1, "one" );
        list1.Add( 2, "two" );
        list2.Add( 3, "three" );
        list2.Add( 4, "four" );
    }
}

I declared two instances of SortedList<TKey, TValue>. In the first instance, I used the parameterless constructor; in the second instance, I explicitly provided a comparer for integers. In both cases, the result is the same because I provided the default generic comparer in the list2 constructor. I did this mainly so you could see the syntax used to pass in the default generic comparer. You could have just as easily provided any other type in the type parameter list for Comparer as long as it supports either IComparable or IComparable<T>.

Generic System Interfaces

Given the fact that the runtime library provides generic versions of container types, it should be no surprise that it also provides generic versions of commonly used interfaces. This is a great thing for those trying to achieve maximum type safety. For example, your classes and structs can implement IComparable<T> and/or IComparable as well as IEquatable<T>. Naturally, IComparable<T> is a more type-safe version of IComparable and should be preferred whenever possible.

Note

IEquatable<T> was added in .NET 2.0 and provides a type-safe interface through which you can perform equality comparisons on value types or reference types.

The System.Collections.Generic namespace also defines a whole host of interfaces that are generic versions of the ones in System.Collections. These include ICollection<T>, IDictionary<TKey, TValue>, and IList<T>. Two of these interfaces deserve special mention: IEnumerator<T> and IEnumerable<T>.[42] The development team at Microsoft decided it would be a good idea for IEnumerator<T> to derive from IEnumerator and for IEnumerable<T> to derive from IEnumerable. This decision has proven to be a controversial one. Anders Hejlsberg, the father of the C# language, indicates that IEnumerable<T> inherits from IEnumerable because it can.

His argument goes something like this: you can imagine that it would be nice if the container that implements IList<T> also implemented IList. If IList<T> inherits from IList, it would be forced upon the author of the container to implement two versions of the Add method: Add<T> and Add. If the end user can call the nongeneric Add, the whole benefit of added type safety through IList<T> would be lost because the very existence of Add opens up the container implementation for runtime cast exceptions. So deriving IList<T> from IList is a bad idea. IEnumerable<T> and IEnumerator<T>, on the other hand, differ from the other generic interfaces in that the type T is used only in return value positions. Therefore, no type safety is lost when implementing both.

Note

This is also another example of covariance.

That is the basis of the justification for saying that IEnumerable<T> can derive from IEnumerable and that IEnumerator<T> can derive from IEnumerator because they can. One of the developers at Microsoft working on the Framework library indicated that IEnumerable<T> and IEnumerator<T> are implemented this way in order to work around the lack of covariance with regard to generics. Yes, it's dizzying indeed. However, that point is moot because C# 4.0 introduced syntax that allows one to implement covariant generic interfaces.

Coding a type that implements IEnumerable<T> requires a bit of a trick in that you must implement the IEnumerable method using explicit interface implementation. Moreover, in order to keep the compiler from becoming confused, you might have to fully qualify IEnumerable with its namespace, as in the following example:

using System;
using System.Collections.Generic;

public class MyContainer<T> : IEnumerable<T>
{
    public void Add( T item ) {
        impl.Add( item );
    }

    public void Add<R>( MyContainer<R> otherContainer,
                        Converter<R, T> converter ) {
        foreach( R item in otherContainer ) {
            impl.Add( converter(item) );
        }
    }
public IEnumerator<T> GetEnumerator() {
        foreach( T item in impl ) {
            yield return item;
        }
    }

    System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }

    private List<T> impl = new List<T>();
}

Select Problems and Solutions

In this section, I want to illustrate some examples of creating generic types that show some useful techniques when creating generic code. I assure you that the pathway to learning how to use generics effectively will contain many surprises from time to time because you must sometimes develop an unnatural or convoluted way of doing something that conceptually is very natural.

Note

Many of you will undoubtedly get that unnatural feeling if you're transitioning from the notion of C++ templates to generics, as you discover the constraints that the dynamic nature of generics places upon you.

Conversion and Operators within Generic Types

Converting from one type to another or applying operators to parameterized types within generics can prove to be tricky. To illustrate, let's develop a generic Complex struct that represents a complex number. Suppose that you want to be able to designate what value type is used internally to represent the real and imaginary portions of a complex number. This example is a tad contrived because you would normally represent the components of an imaginary number using something such as System.Double. However, for the sake of example, let's imagine that you might want to be able to represent the components using System.Int64. (Throughout this discussion, in order to reduce clutter and focus on the issues regarding generics, I'm going to ignore all the canonical constructs that the generic Complex struct should implement.)

You could start out by defining the Complex number as follows:

using System;

public struct Complex<T>
    where T: struct
{
    public Complex( T real, T imaginary ) {
        this.real = real;
        this.imaginary = imaginary;
}

    public T Real {
        get { return real; }
        set { real = value; }
    }

    public T Img {
        get { return imaginary; }
        set { imaginary = value; }
    }

    private T real;
    private T imaginary;
}

public class EntryPoint
{
    static void Main() {
        Complex<Int64> c =
            new Complex<Int64>( 4, 5 );
    }
}

This is a good start, but now let's make this value type a little more useful. You could benefit from having a Magnitude property that returns the square root of the two components multiplied together. Let's attempt to create such a property:

using System;

public struct Complex<T>
    where T: struct
{
    public Complex( T real, T imaginary ) {
        this.real = real;
        this.imaginary = imaginary;
    }

    public T Real {
        get { return real; }
        set { real = value; }
    }

    public T Img {
        get { return imaginary; }
        set { imaginary = value; }
    }

    public T Magnitude {
        get {
            // WON'T COMPILE!!!
            return Math.Sqrt( real * real +
                              imaginary * imaginary );
}
    }

    private T real;
    private T imaginary;
}

public class EntryPoint
{
    static void Main() {
        Complex<Int64> c =
            new Complex<Int64>( 3, 4 );

        Console.WriteLine( "Magnitude is {0}",
                           c.Magnitude );
    }
}

If you attempt to compile this code, you might be surprised to get the following compiler error:

error CS0019: Operator '*' cannot be applied to operands of type 'T' and 'T'

This is a perfect example of the problem with using operators in generic code. The compilation problem stems from the fact that you must compile generic code in a generic way because constructed types formed at runtime can be formed from a value type that might not support the operator. In this case, it's impossible for the compiler to know whether the type given for T in a constructed type at some point in the future even supports the multiplication operator. What are you to do? A common technique is to externalize the operation from the Complex<T> definition and then require the user of Complex<T> to provide the operation. A delegate is the perfect tool for doing this. Let's look at an example of Complex<T> that does that:

using System;

public struct Complex<T>
    where T: struct, IConvertible
{
    // Delegate for doing multiplication.
    public delegate T BinaryOp( T val1, T val2 );

    public Complex( T real, T imaginary,
                    BinaryOp mult,
                    BinaryOp add,
                    Converter<double, T> convToT ) {
        this.real = real;
        this.imaginary = imaginary;
        this.mult = mult;
        this.add = add;
        this.convToT = convToT;
    }

    public T Real {
get { return real; }
        set { real = value; }
    }

    public T Img {
        get { return imaginary; }
        set { imaginary = value; }
    }

    public T Magnitude {
        get {
            double magnitude =
                Math.Sqrt( Convert.ToDouble(add(mult(real, real),
                                                mult(imaginary, imaginary))) );
            return convToT( magnitude );
        }
    }

    private T real;
    private T imaginary;
    private BinaryOp mult;
    private BinaryOp add;
    private Converter<double, T> convToT;
}

public class EntryPoint
{
    static void Main() {
        Complex<Int64> c =
            new Complex<Int64>(
                    3, 4,
                    EntryPoint.MultiplyInt64,
                    EntryPoint.AddInt64,
                    EntryPoint.DoubleToInt64 );

        Console.WriteLine( "Magnitude is {0}",
                           c.Magnitude );
    }

    static Int64 MultiplyInt64( Int64 val1, Int64 val2 ) {
        return val1 * val2;
    }

    static Int64 AddInt64( Int64 val1, Int64 val2 ) {
        return val1 + val2;
    }

    static Int64 DoubleToInt64( double d ) {
        return Convert.ToInt64( d );
    }
}

You're probably looking at this code and wondering what went wrong and why the complexity seems so much higher when all you're trying to do is find the contrived definition of the magnitude of a complex number. As mentioned previously, you had to provide a delegate to handle the multiplication external to the generic type. Thus, I've defined the Complex<T>.Multiply delegate. At construction time, the Complex<T> constructor must be passed a third parameter that references a method for the multiplication delegate to refer to. In this case, EntryPoint.MultiplyInt64 handles multiplication. So, when the Magnitude property needs to multiply the components, it must use the delegate rather than the multiplication operator. Naturally, when the delegate is called, it boils down to a call to the multiplication operator. However, the application of the operator is now effectively external to the generic type Complex<T>. And as you can see, I applied the same technique for the add operation.

No doubt you have noticed the extra complexities in the property accessor. First, Math.Sqrt accepts a type of System.Double. This explains the call to the Convert.ToDouble method. And to make sure things go smoothly, I added a constraint to T so that the type supplied supports IConvertible. But you're not done yet. Math.Sqrt returns a System.Double, and you have to convert that value type back into type T. In order to do so, you cannot rely on the System.Convert class because you don't know what type you're converting to at compile time. Yet again, you have to externalize an operation, which in this case is a conversion. This is precisely one reason why the Framework defines the Converter<TInput, TOuput> delegate. In this case, Complex<T> needs a Converter<double, T> conversion delegate. At construction time, you must pass a method for this delegate to call through to, which in this case is EntryPoint.DoubleToInt64. Now, after all this, the Complex<T>.Magnitude property works as expected, but not without an extra amount of work.

Note

The complexity of using Complex<T>, as shown in the previous example, is greatly reduced by using lambda expressions, which are covered fully in Chapter 15. By using lambda expressions, you can completely bypass the need to define the operation methods such as MultiplyInt64, AddInt64, and DoubeToInt64, as shown in the example.

Let's say you want instances of Complex<T> to be able to be used as key values in a SortedList<TKey, TValue> generic type. In order for that to work, Complex<T> needs to implement IComparable<T>. Let's see what you need to do to make that a reality:

using System;

public struct Complex<T> : IComparable<Complex<T> >
    where T: struct, IConvertible , IComparable
{
    // Delegate for doing multiplication.
    public delegate T BinaryOp( T val1, T val2 );

    public Complex( T real, T imaginary,
                    BinaryOp mult,
                    BinaryOp add,
                    Converter<double, T> convToT ) {
        this.real = real;
        this.imaginary = imaginary;
        this.mult = mult;
        this.add = add;
this.convToT = convToT;
    }

    public T Real {
        get { return real; }
        set { real = value; }
    }

    public T Img {
        get { return imaginary; }
        set { imaginary = value; }
    }

    public T Magnitude {
        get {
            double magnitude =
                Math.Sqrt( Convert.ToDouble(add(mult(real, real),
                                                mult(imaginary, imaginary))) );
            return convToT( magnitude );
        }
    }

    public int CompareTo( Complex<T> other ) {
    return Magnitude.CompareTo( other.Magnitude );
    }

    private T real;
    private T imaginary;
    private BinaryOp mult;
    private BinaryOp add;
    private Converter<double, T> convToT;
}

public class EntryPoint
{
    static void Main() {
        Complex<Int64> c =
            new Complex<Int64>(
                    3, 4,
                    EntryPoint.MultiplyInt64,
                    EntryPoint.AddInt64,
                    EntryPoint.DoubleToInt64 );

        Console.WriteLine( "Magnitude is {0}",
                           c.Magnitude );
    }

    static Int64 MultiplyInt64( Int64 val1, Int64 val2 ) {
        return val1 * val2;
    }

    static Int64 AddInt64( Int64 val1, Int64 val2 ) {
        return val1 + val2;
}

    static Int64 DoubleToInt64( double d ) {
        return Convert.ToInt64( d );
    }
}

My implementation of the IComparable<Complex<T>> interface considers two Complex<T> types to be equivalent if they have the same magnitude. Therefore, most of the work required to do the comparison is done already. However, instead of being able to rely upon the inequality operator of the C# language, again you need to use a mechanism that doesn't rely upon operators. In this case, I used the CompareTo method. Of course, this requires me to force another constraint on type T: it must support the nongeneric IComparable interface because the type provided for T might not even be generic at all, thus it might support only IComparable rather than IComparable<T>.

One thing worth noting is that the previous constraint on the nongeneric IComparable interface makes it a little bit difficult for Complex<T> to contain generic structs because generic structs might implement IComparable<T> instead. In fact, given the current definition, it is impossible to define a type of Complex<Complex<int>>. It would be nice if Complex<T> could be constructed from types that might implement either IComparable<T> or IComparable, or even both. Let's see how you can do this:

using System;
using System.Collections.Generic;

public struct Complex<T> : IComparable<Complex<T> >
    where T: struct
{
    // Delegate for doing multiplication.
    public delegate T BinaryOp( T val1, T val2 );

    public Complex( T real, T imaginary,
                    BinaryOp mult,
                    BinaryOp add,
                    Converter<double, T> convToT ) {
        this.real = real;
        this.imaginary = imaginary;
        this.mult = mult;
        this.add = add;
        this.convToT = convToT;
    }

    public T Real {
        get { return real; }
        set { real = value; }
    }

    public T Img {
        get { return imaginary; }
        set { imaginary = value; }
    }

    public T Magnitude {
        get {
            double magnitude =
Math.Sqrt( Convert.ToDouble(add(mult(real, real),
                                                mult(imaginary, imaginary))) );
            return convToT( magnitude );
        }
    }

    public int CompareTo( Complex<T> other ) {
        return Comparer<T>.Default.Compare( this.Magnitude, other.Magnitude );
    }

    private T real;
    private T imaginary;
    private BinaryOp mult;
    private BinaryOp add;
    private Converter<double, T> convToT;
}

public class EntryPoint
{
    static void Main() {
        Complex<Int64> c =
            new Complex<Int64>(
                    3, 4,
                    EntryPoint.MultiplyInt64,
                    EntryPoint.AddInt64,
                    EntryPoint.DoubleToInt64 );

        Console.WriteLine( "Magnitude is {0}",
                           c.Magnitude );
    }

    static void DummyMethod( Complex<Complex<int> > c ) {
    }

    static Int64 AddInt64( Int64 val1, Int64 val2 ) {
        return val1 + val2;
    }

    static Int64 MultiplyInt64( Int64 val1, Int64 val2 ) {
        return val1 * val2;
    }

    static Int64 DoubleToInt64( double d ) {
        return Convert.ToInt64( d );
    }
}

In this example, I had to remove the constraint on T requiring implementation of the IComparable interface. Instead, the CompareTo method relies upon the default generic comparer defined in the System.Collections.Generic namespace.

Note

The generic comparer class Comparer<T> introduces one more level of indirection in the form of a class with regard to comparing two instances. In effect, it externalizes the comparability of the instances. If you need a custom implementation of IComparer, you should derive from Comparer<T>.

Additionally, I had to remove the IConvertible constraint on T to get DummyMethod to compile. That's because Complex<T> doesn't implement IConvertible, and when T is replaced with Complex<T> (thus forming Complex<Complex<T>>), the result is that T doesn't implement IConvertible.

Note

When creating generic types, try not to be too restrictive by forcing too many constraints on the contained types. For example, don't force all the contained types to implement IConvertible. Many times, you can externalize such constraints by using a helper object coupled with a delegate.

Think about the removal of this constraint for a moment. In the Magnitude property, you rely on the Convert.ToDouble method. However, because you removed the constraint, the possibility of getting a runtime exception exists—for example, when the type represented by T doesn't implement IConvertible. Because generics are meant to provide better type safety and help you avoid runtime exceptions, there must be a better way. In fact, there is and you can do better by giving Complex<T> yet another converter in the form of a Convert<T, double> delegate in the constructor, as follows:

using System;
using System.Collections.Generic;

public struct Complex<T> : IComparable<Complex<T> >
    where T: struct
{
    // Delegate for doing multiplication.
    public delegate T BinaryOp( T val1, T val2 );

    public Complex( T real, T imaginary,
                    BinaryOp mult,
                    BinaryOp add,
                    Converter<T, double> convToDouble,
                    Converter<double, T> convToT ) {
        this.real = real;
        this.imaginary = imaginary;
        this.mult = mult;
        this.add = add;
        this.convToDouble = convToDouble;
        this.convToT = convToT;
    }

    public T Real {
        get { return real; }
set { real = value; }
    }

    public T Img {
        get { return imaginary; }
        set { imaginary = value; }
    }

    public T Magnitude {
        get {
            double magnitude =
                Math.Sqrt( convToDouble(add(mult(real, real),
                                            mult(imaginary, imaginary))) );
            return convToT( magnitude );
        }
    }

    public int CompareTo( Complex<T> other ) {
        return Comparer<T>.Default.Compare( this.Magnitude, other.Magnitude );
    }

    private T real;
    private T imaginary;
    private BinaryOp mult;
    private BinaryOp add;
    private Converter<T, double> convToDouble;
    private Converter<double, T> convToT;
}

public class EntryPoint
{
    static void Main() {
        Complex<Int64> c =
            new Complex<Int64>(
                    3, 4,
                    EntryPoint.MultiplyInt64,
                    EntryPoint.AddInt64,
                    EntryPoint.Int64ToDouble,
                    EntryPoint.DoubleToInt64 );

        Console.WriteLine( "Magnitude is {0}",
                           c.Magnitude );
    }

    static void DummyMethod( Complex<Complex<int> > c ) {
    }

    static Int64 MultiplyInt64( Int64 val1, Int64 val2 ) {
        return val1 * val2;
    }

    static Int64 AddInt64( Int64 val1, Int64 val2 ) {
        return val1 + val2;
}

    static Int64 DoubleToInt64( double d ) {
        return Convert.ToInt64( d );
    }

    static double Int64ToDouble( Int64 i ) {
        return Convert.ToDouble( i );
    }
}

Now, the Complex<T> type can contain any kind of struct, whether it's generic or not. However, you must provide it with the necessary means to be able to convert to and from double as well as to multiply and add constituent types. This Complex<T> struct is by no means meant to be a reference for complex number representation at all. Rather, it is a somewhat contrived example meant to illustrate many of the concerns you must deal with in order to create effective generic types.

You'll see some of these techniques in practice as you deal with the generic containers that exist in the BCL.

Creating Constructed Types Dynamically

Given the dynamic nature of the CLR and the fact that you can actually generate classes and code at runtime, it is only natural to consider the possibility of constructing closed types from generics at runtime. Until now, all the examples in this book have dealt with creating closed types at compile time.

This functionality stems from a natural extension of the metadata specification to accommodate generics. System.Type is the cornerstone of functionality whenever you need to work with types dynamically within the CLR, so it has been extended to deal with generics as well. Some of the generic-centric methods on System.Type are self-explanatory by name and include GetGenericArguments, GetGenericParameterConstraints, and GetGenericTypeDefinition. These methods are helpful when you already have a System.Type instance representing a closed type. However, the method that makes things interesting is MakeGenericType, which allows you to pass an array of System.Type objects that represent the types that are to be used in the argument list for the resultant constructed type.

Those coming from a C++ template background have probably become frustrated from time to time with generics because they lack the static compile-time capabilities of templates. However, I think you'll agree that the dynamic capabilities of generics make up for that in the end. Imagine how handy it is to be able to create closed types from generics at runtime. For example, creating a parsing engine for some sort of XML-based language that defines new types from generics is a snap. Let's take a look at an example of how to use the MakeGenericType method:

using System;
using System.Collections.Generic;

public class EntryPoint
{
    static void Main() {
        IList<int> intList =
            (IList<int>) CreateClosedType<int>( typeof(List<>) );

        IList<double> doubleList =
            (IList<double>)
                CreateClosedType<double>( typeof(List<>) );
Console.WriteLine( intList );
        Console.WriteLine( doubleList );
    }

    static object CreateClosedType<T>( Type genericType ) {
        Type[] typeArguments = {
            typeof( T )
        };

        Type closedType =
            genericType.MakeGenericType( typeArguments );

        return Activator.CreateInstance( closedType );
    }
}

The meat of this code is inside the generic method CreateClosedType<T>. All the work is done in general terms via references to Type created from the available metadata. First, you need to get a reference to the generic open type List<>, which is passed in as a parameter. After that, you simply create an array of Type instances to pass to MakeGenericType to obtain a reference to the closed type. Once that stage is complete, the only thing left to do is to call CreateInstance on the System.Activator class. System.Activator is the facility that you must use to create instances of types that are known only at runtime. In this case, I'm calling the default constructor for the closed type. However, Activator has overloads of CreateInstance that allow you to call constructors that require parameters.

Note

I used the C# typeof operator rather than the Type.GetType method to obtain the Type instance for the types. If the type is known at compile time, the typeof operator performs the metadata lookup then rather than at runtime—therefore, it is more efficient.

When you run the previous example, you'll see that the closed types are streamed to the console showing their fully qualified type names, thus proving that the closed types were created properly after all.

The ability to create closed types at runtime is yet another powerful tool in your toolbox for creating highly dynamic systems. Not only can you declare generic types within your code so that you can write flexible code, but you can also create closed types from those generic definitions at runtime. Take a moment to consider the extent of problems you could solve with these techniques, and it's easy to see that generics are extremely potent.

Summary

This chapter has discussed how to declare and use generics using C#, including generic classes, structs, interfaces, methods, and delegates. I discussed generic constraints, which are necessary for the compiler to create code where certain functional assumptions are placed upon the type arguments provided for the generic type arguments at runtime. Collection types enjoy a real and measurable gain in efficiency and safety with generics.

Not only do generics allow you to generate more efficient code when using value types with containers but they also give the compiler much more power when enforcing type safety. As a rule, you should always prefer compile-time type safety over runtime type safety. You can fix a compile-time failure before software is deployed, but a runtime failure usually results in an InvalidCastException thrown in a production environment. Such a runtime failure could cost the end user huge sums of money, depending on the situation, and it could cause large amounts of embarrassment for you as the developer. Therefore, always provide the compiler with as much power as possible to enforce type safety, so it can do what it's meant to do best—and that's to be your friend.

The next chapter tackles the topic of threading in C# and the .NET runtime. Along with threading comes the ever-so-important topic of synchronization.



[41] I covered iterators fully in Chapter 9. Also, if you're wondering why there is an explicitly implemented version of GetEnumerator that returns a nongeneric IEnumerator, it is because IEnumerable<T> derives from IEnumerable.

[42] Chapter 9 covers the facilities provided by IEnumerator<T> and IEnumerable<T> and how you can implement them easily by using C# iterators.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.21.158