我有一个基类:
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());
}
这两种方法虽然在语法上有所不同,但却以同样的方式出现。
他们只是做同样事情的不同方式吗?我理解泛型和类型约束,但不明白为什么我会在这种情况下使用一种方式而不是另一种方式。
答案 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);
第一个调用运行良好,但第二个调用由于通用协方差而无法编译