什么时候应该或不应该使用泛型类型约束?

时间:2009-08-02 19:10:50

标签: c# generics c#-2.0 type-constraints

我有一个基类:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

两个派生类

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

好的,现在说我有一个项目列表:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

我希望他们都能调用他们的DoSomething()方法。我可以期望只是迭代列表并调用它们的DoSomething()方法,所以让我们说我有一个方法来做这个叫做AllDoSomething()的方法只是遍历列表并完成工作:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

以下方法的实际区别是什么?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

这两种方法虽然在语法上有所不同,但却以同样的方式出现。

他们只是做同样事情的不同方式吗?我理解泛型和类型约束,但不明白为什么我会在这种情况下使用一种方式而不是另一种方式。

3 个答案:

答案 0 :(得分:6)

这是因为到目前为止,C#不支持Covariance

  

更正式地说,如果T是a,则在C#v2.0中   U的子类型,则T []是子类型   U [],但G不是G的子类型   (其中G是任何泛型类型)。在   我们描述了类型理论术语   这种行为通过说C#数组   类型是“协变”和通用的   类型是“不变的”。

参考:http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

如果您有以下方法:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

如果您使用泛型类型约束,它将会。

有关协方差和反差异的更多信息,请查看Eric Lippert's series of posts


其他值得一读的帖子:

答案 1 :(得分:1)

假设你有一个清单:

List<Stuff1> l = // get from somewhere

现在尝试:

AllDoSomething(l);

使用通用版本,将允许。使用非泛型,它不会。这是本质区别。 Stuff1列表不是StuffBase的列表。但在通用情况下,您并不要求它只是StuffBase的列表,因此它更灵活。

您可以首先将Stuff1列表复制到StuffBase列表中,以使其与非通用版本兼容。但是假设你有一个方法:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

如果没有泛型,您可以接受StuffBase的列表,但是您必须返回StuffBase的列表。如果调用者知道这些项目实际上是派生类型,则必须使用强制转换。因此,泛型允许您保留参数的实际类型,并通过该方法将其引导到返回类型。

答案 2 :(得分:0)

在您提供的示例中没有区别,但请尝试以下操作:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

第一个调用运行良好,但第二个调用由于通用协方差而无法编译