2

我有一个基类:

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());
}

这两种方法虽然在语法上有所不同,但实际上都是在做同样的事情。

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

4

3 回答 3

6

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

更正式地说,在 C# v2.0 中,如果 T 是 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 的系列文章


其他值得一读的帖子:

于 2009-08-02T19:24:59.760 回答
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。如果调用者知道项目确实是派生类型,则调用者将不得不使用强制类型转换。因此泛型允许您保留参数的实际类型并通过方法将其引导到返回类型。

于 2009-08-02T19:27:25.810 回答
0

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

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

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

于 2009-08-02T19:31:07.797 回答