在方法签名中使用泛型有什么好处?

时间:2009-03-18 15:26:46

标签: c# generics

(感谢大家的答案here is my refactored example,反过来另一个关于单一责任原则的StackOverflow问题。)

从PHP到C#,这种语法令人生畏:

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

直到我意识到它表达了同样的事情:

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

正如我在下面的代码中演示的那样。

所以是否有一些理由为什么在这里使用泛型(例如整个Unity和大多数C#IoC容器),除了它只是一个更清晰的语法,即你不需要typeof()时发送类型?

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

7 个答案:

答案 0 :(得分:5)

主要原因是编译时的类型安全性。如果要传递两个Type对象,则将责任放在开发人员而不是编译器上。

这也是许多IoC容器使用它的原因,因为如果具体类型没有继承抽象类型,编译器会抱怨。

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

此代码仅在TConcrete正在实施或继承TAbstract时才有效。如果此方法采用两个Type参数,则您的方法应验证此关系。

答案 1 :(得分:4)

泛型非常有用的一个原因是泛型类型参数用作参数的类型或方法的返回类型。 这意味着,您可以编写类似

的方法
public T GetAs<T>(string name)

其中返回类型可以由编译器检查,有时可以避免装箱值类型。 来电者会写:

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

没有泛型,你必须写

public object GetAs(Type t, string name)

并且调用者必须再次转换结果:

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

答案 2 :(得分:4)

一个简单的答案是类型推断尽可能。

如果在方法签名中使用泛型类型,则可以省略它,因为可以推断出类型:

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

因此简化了用法:

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

如果方法签名中未使用泛型类型参数,则无法进行类型推断:

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

答案 3 :(得分:3)

例如,使用typeof选项与通用选项比较创建类型实例所需的代码。或者返回该类型的实例。或者接受类型的实例作为参数。或者在类型的实例上设置属性。

通常,如果您只使用类型本身,则可以接受类型参数。如果要对类型的实例执行任何操作,请使用泛型。

使用泛型的另一个原因是如果要对类型应用约束。例如,您可以要求类型实现一个或多个接口,继承另一个类型,作为引用类型或值类型,具有默认构造函数或上述的某种组合。编译器将强制执行此操作,因此您无法构建不符合您要求的代码。

答案 4 :(得分:3)

我认为其中一个主要用途是带有参数和返回值的类型安全。在您的示例中,泛型没有太多用处,因为输入/输出类型(字符串)与通用情况(客户)不匹配。

更合适的用途可能是:

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

或者

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

答案 5 :(得分:2)

如果您没有使用泛型,则必须为要支持的每种类型重载方法,或者您必须接受该参数作为对象并执行转换逻辑。

答案 6 :(得分:1)

我认为最好的原因是类型安全,使用“where”关键字,以确保泛型类型是某种类型(或子类/实现者)。使用“typeof”将允许您发送任何内容。