Generics were a great addition to .NET but sometimes their behavior can be unexpected. Check out these somewhat unintuitive bits.

Static Constructors

Consider the following class with a static constructor:

public class GenericDemo<T>
    {
        static GenericDemo()
        {
                Console.WriteLine("static (shared) constructor");
        }


        public void SayHello()
        {
            Console.WriteLine("Hello!");
        }

    }

Given the following usage of the type, how many times is the shared constructor called?

    1:  public static void Main()
    2:   {
    3: 

    4:   GenericDemo<string> instance1 = new GenericDemo<string>();
    5: 

    6:   GenericDemo<string> instance2 = new GenericDemo<string>();
    7: 

    8:   GenericDemo<int> instance3 = new GenericDemo<int>();
    9: 

   10:   GenericDemo<float> instance5;
   11: 

   12:   GenericDemo<double>.SayHello();
   13: 

   14:   }

The answer is 3 which makes sense if you think about it.

This gets at the heart of what generics really are. Static constructors are only called once (single threaded assumed) per type when first instantiated or when invoking a static method. So lines 4, 8, and 12 exclusively manipulate new types for the first time and trigger the call.

Overloading

Consider the following declaration.

public class GenericDemo2<T>
    {

        public void SayHello(T myinput)
        {
            Console.WriteLine("generic overload invoked");
        }

        public void SayHello(string myinput)
        {
            Console.WriteLine("string overload invoked");
        }

    }

Will it compile? It surprised me that this is indeed valid and does not throw a runtime error if T is a string. If called when T is a string, it calls the non generic method every time. Interestingly VB does not allow this to compile because its implementation varies slightly.

How about this which uses generic methods:

public class Example3
    {
        public void SayHello(string toSay)
        {
            Console.WriteLine(toSay);
        }

        public void SayHello<T>(string toSay)
        {
            Console.WriteLine(toSay);
        }
    }

It looks like there would be a conflict and the compiler would reject the overload. But it does compile! The T is a true parameter and gives it a different signature!