List Generics - 隐式转换

Ste*_*bbi 7 .net generics casting implicit-conversion

我的目标是.NET 3.5.假设我有一个类Bob,它是SubBob的抽象基类.

我可以声明这个:

Bob b = new SubBob();
Run Code Online (Sandbox Code Playgroud)

但我不能这样做:

 // compliation error - can't convert
BindingList<Bob> myList = new BindingList<SubBob>(); 
Run Code Online (Sandbox Code Playgroud)

我的猜测是BindingList不希望你这样做,因为它必须知道右侧的类型与左侧的内存布局相同.SubBob的大小可能比Bob大.

有没有办法可以进行隐式转换,还是需要强制转换?

Dan*_*mov 6

简答

通过实例BindingList<SubBob>你限制它的工作SubBob更具体的类型(例如SubSubBob).

如果您想要Bob适合那里,请声明myList您要支持的最不具体类型的列表:

BindingList<Bob> myList = new BindingList<Bob>(); 
Run Code Online (Sandbox Code Playgroud)

(或者,更方便)

var myList = new BindingList<Bob>(); 
Run Code Online (Sandbox Code Playgroud)

说明

它不是关于内存(BindingList只包含对象的引用,并且所有引用都具有相同的大小),而是关于您将引入的逻辑不一致.

如果可以使用此类代码,您可以随意破坏类型限制:

BindingList<Animal> myList = new BindingList<Cat>(); 
myList.Add(new Dog()); // bang!
Run Code Online (Sandbox Code Playgroud)

myList是一个Cats 的列表,你期望它如何处理Dog

编译器不会知道存在问题,并且很乐意编译您的代码.这段代码运行时会发生什么?例外?但是引入了仿制药来解决类型安全问题.

关于共同和逆差的侧面说明

在.NET 4.0中,为代理和接口(不是类)添加了通用协方差和逆变.例如,是协变,这意味着可以将其分配给任何类型的可变少衍生比:IEnumerable<out T>T

IEnumerable<Cat> cats = new List<Cat> { new Cat("Hudson"), new Cat("Crookshanks") };
IEnumerable<Animal> animals = cats; // sequence of cats is sequence of animals
Run Code Online (Sandbox Code Playgroud)

但这只是可能的,因为IEnumerable<out T>保证它只返回 T(关键字out)并且永远不接受它.如果它被接受T作为参数,它将打开上述问题的大门.出于这个原因,ICollection不是协变.

以类似的方式,一些接口保证它们只接受 T(关键字in)并且永远不会返回它.这样的接口称为逆变,允许更具体地 赋值给变量T:

IComparer<Animal> animalComparer = // ...
IComparer<Dog> dogComparer = animalComparer; // comparer of animals is comparer of dogs
Run Code Online (Sandbox Code Playgroud)