tags:

views:

426

answers:

7

(Thanks everyone for the answers, here is my refactored example, in turn another StackOverflow question about the Single Responsibility Principle.)

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
+2  A: 

For one example, compare the code needed to create an instance of your type using the typeof option 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.

Joel Coehoorn
+3  A: 

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
}
Matt Murrell
that sounds very useful, but trying to implement it, the line "T obj = new T();" gives me "Cannot create an instance of the variable type 'T' because it does not have the new() constraint". What am I missing?
Edward Tanguay
You need to add a constraint as in my answer: `public T CreateNew<T>() where T : new()`
troethom
+3  A: 

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");
EFrank
+2  A: 

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.

Rich
+5  A: 

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 two Type parameters, your method should validate this relationship.

troethom
+3  A: 

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>();
Michael Damatov
+1  A: 

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.

JonoW