Why generics in method signatures?

Coming from PHP to C#, this syntax was intimidating:

container.RegisterType<Customer>("customer1");

until I realized it expresses the same thing as:

container.RegisterType(typeof(Customer), "customer1");

as I demonstrate in the code below.

So is there some reason why generics is used here (e.g. throughout Unity and most C# IoC containers) other than it just being a cleaner syntax, i.e. you don't need the typeof() when sending the type?


using System;

namespace TestGenericParameter
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            container.RegisterType<Customer>("test");
            container.RegisterType(typeof(Customer), "test");

            Console.ReadLine();
        }
    }

    public class Container
    {
        public void RegisterType<T>(string dummy)
        {
            Console.WriteLine("Type={0}, dummy={1}, name of class={2}", typeof(T), dummy, typeof(T).Name);
        }

        public void RegisterType(Type T, string dummy)
        {
            Console.WriteLine("Type={0}, dummy={1}, name of class={2}", T, dummy, T.Name);
        }

    }

    public class Customer {}
}

//OUTPUT:
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer

Explain 1:

A primary reason is the type safety at compile time. If you are passing two Type objects you are placing the responsibility at the developer instead of the compiler.

This is also why many IoC containers utilizes it, as your compiler will complain if an concrete type isn't inheriting the abstract type.

public void Register<TAbstract, TConcrete>() where TConcrete : TAbstract
{
}

This code will only work if TConcrete is implementing or inheriting TAbstract. If this method took twoType parameters, your method should validate this relationship.


Explain 2:

One reason when generics are very useful is when the generic type parameter is used as the type of a parameter or as the return type of the method. That means, you can write methods like

public T GetAs<T>(string name)

where the return type can be checked by the compiler and boxing value types can sometimes be avoided. The caller would write:

int value = GetAs<int>("foo");

Whithout generics, you would have to write

public object GetAs(Type t, string name)

and the caller has to cast the result again:

int value = (int)GetAs(typeof(int), "foo");

Explain 3:

A simple answer is type inference where possible.

If the generic type is used in the method signature, you can omit it because the type could be inferred:

void SomeMethod<T>(T x, T y) where T : IComparable<T> {
    Console.WriteLine("Result: {0} to {1} is {2}", x, y, x.CompareTo(y));
}

So the usage is simplified:

SomeMethod(3, 4);         // instead of SomeMethod<int>(3, 4);
SomeMethod("one", "two"); // instead of SomeMethod<string>("one", "two");

If the generic type parameter is not used in the method signature the type inference is not possible:

var emptySequence = Enumerable.Empty<int>();


Explain 4:

I think one of the primary uses is type safety with arguments and return values. In your example case, there is not much use for generics, because the input/output types (string) do not match the generic case (customers).

A more appropriate use might be:

public T RegisterType<T>(string name) 
{
    T obj = new T();
    obj.DoSomething();
    return obj;
}

or maybe

public void DoSomething<T>(T obj) 
{
    //operate on obj
}


Explain 5:

For one example, compare the code needed to create an instance of your type using the typeofoption versus a generic. Or return an instance of the type. Or accept an instance of the type as an argument. Or set a property on an instance of the type.

In general, if you will be working only with the type itself you can accept a type parameter. If you want to do anything with an instance of the type, use a generic.

Another reason to use a generic is if you want to apply constraints to the type. For example, you can require the type to implement one or several interfaces, inherit another type, be a reference type or value type, have a default constructor, or some combination of the above. The compiler will enforce this so you can't build code that doesn't comply with your requirements.


Explain 6:

If you didn't use Generics, you'd either have to overload a method for each type you want to support, or you'd have to accept the parameter as an object and perform casting logic.


Explain 7:

I'd say the best reason is type safety, using the "where" keyword, to ensure that the generic type is of a certain type (or sub-class/implementor). Using "typeof" will let you send anything through.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章