为什么我不能将List 分配给List ?
我定义了以下类:
public abstract class AbstractPackageCall { ... }
我还定义了这个类的子类:
class PackageCall : AbstractPackageCall { ... }
AbstractPackageCall
还有其他几个子节点
现在我想进行以下调用:
List calls = package.getCalls();
但我总是得到这个例外:
Error 13 Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'
这里有什么问题? 这是Package#getCalls的方法
internal List getCalls() { return calls; }
理解为什么不允许这样做的最简单方法是以下示例:
abstract class Fruit { } class Apple : Fruit { } class Banana : Fruit { } // This should intuitively compile right? Cause an Apple is Fruit. List fruits = new List(); // But what if I do this? Adding a Banana to a list of Apples fruits.Add(new Banana());
最后一句话会破坏.NET的类型安全性。
但是,数组允许这样做:
Fruit[] fruits = new Apple[10]; // This is perfectly fine
然而,将Banana
放入fruits
仍会破坏类型安全性,因此.NET必须对每个数组插入进行类型检查,如果它实际上不是Apple
,则抛出exception。 这可能是一个(小)性能损失,但这可以通过在任一类型周围创建struct
包装器来规避,因为这种检查不会发生在值类型中(因为它们不能从任何东西inheritance)。 起初,我不明白为什么做出这个决定,但你会经常遇到为什么这个有用。 最常见的是String.Format
,它接受params object[]
并且任何数组都可以传递给它。
但是在.NET 4中,存在类型安全协方差/逆变,它允许您进行类似这样的分配,但前提是它们可certificate是安全的。 什么是可靠的安全?
IEnumerable fruits = new List();
以上工作在.NET 4中,因为IEnumerable
变为IEnumerable
。 out
意味着T
只能从 fruits
出来 ,并且在IEnumerable
上根本没有任何方法将T
作为参数,所以你永远不能错误地将Banana
传递给IEnumerable
。
逆差异大致相同,但我总是忘记它的具体细节。 不出所料,为此现在在类型参数上有in
关键字。
现在,如果您想进行以下调用:
List calls = package.getCalls(); // select only AbstractPackageCall items List calls = calls.Select(); calls.Add(new AnotherPackageCall());
我称之为概括。
此外,您可以使用更具体的:
List calls = package.getCalls(); // select only AnotherPackageCall items List calls = calls.Select(); calls.Add(new AnotherPackageCall());
使用扩展方法实现此方法:
public static class AbstractPackageCallHelper { public static List Select(this List source) where T : AbstractPackageCall where U : T { List target = new List(); foreach(var element in source) { if (element is U) { target.Add((U)element); } } return target; } }
你为什么尝试不起作用
您要求编译器将List
视为List
,而不是。 另一个问题是,每个PackageCall
实例实际上都是AbstractPackageCall
。
什么会起作用
var calls = package.getCalls().Cast().ToList();
为什么你的尝试永远不会被允许工作
即使package.getCalls()
的返回值内的每个项都派生自AbstractPackageCall
,我们也不能将整个列表视为List
。 如果我们能够:
var calls = package.getCalls(); // calls is List List apcs = calls; // ILLEGAL, but assume we could do it apcs.Add(SomeOtherConcretePackageCall()); // BOOM!
如果我们可以这样做,那么我们可以将SomeOtherConcretePackageCall
添加到List
。
因为List
不从List
inheritance。 您可以使用Cast<>()
扩展方法,如:
var calls = package.getCalls().Cast();
您无法执行此转换,因为List
可以包含从AbstractPackageCall
派生的任何AbstractPackageCall
,而List
则不能 – 它只能包含从PackageCall
派生的内容。
考虑是否允许此演员:
class AnotherPackageCall : AbstractPackageCall { } // ... List calls = package.getCalls(); calls.Add(new AnotherPackageCall());
您刚刚将AnotherPackageCall
添加到List
– 但AnotherPackageCall
不是从AnotherPackageCall
派生的! 这就是为什么不允许这种转换的原因。
- 如何在.NET中从大文件的中间插入或删除字节
- 覆盖代码生成的DbContext构造函数
- 在应用程序启动时调用XNA GraphicsDeviceManager.ApplyChanges()会导致闪烁
- 如何使用.Netreflection按名称忽略大小写来搜索属性?
- Visual Studiounit testingAssert.AreEqual失败,具有相同的预期值和实际值
- LogicalOperationStack与.Net 4.5中的异步不兼容
- WebBrowser从我的表单“窃取”KeyDown事件
- MongoDB如何检查是否存在
- 如何在Windows窗体C中添加对Visual Basic电源包的引用#