我的目标是 .NET 3.5。假设我有一个类 Bob,它是 SubBob 的抽象基类。
我可以声明:
Bob b = new SubBob();
但我不能这样做:
// compliation error - can't convert
BindingList<Bob> myList = new BindingList<SubBob>();
我的猜测是 BindingList 不希望您这样做,因为它必须知道右侧的类型与左侧的类型具有相同的内存布局。 SubBob 的尺寸可能比 Bob 大。
有没有办法可以进行隐式转换,还是需要强制转换?
最佳答案
简短的回答
通过实例化 BindingList<SubBob>
您可以将其限制为使用 SubBob
和更具体的类型(例如 SubSubBob
)。
如果您希望 Bob
也适合那里,请将 myList
声明为 的列表,这是您想要支持的最不具体的类型 :
BindingList<Bob> myList = new BindingList<Bob>();
(或者,更方便的是,)
var myList = new BindingList<Bob>();
解释
它与内存无关(
BindingList
只会保存对对象的引用,并且所有引用的大小都相同),而是关于您将引入的逻辑不一致。如果这样的代码是可能的,你将能够 任意打破类型限制 :
BindingList<Animal> myList = new BindingList<Cat>();
myList.Add(new Dog()); // bang!
myList
是 Cat
的列表,您希望它如何处理 Dog
?编译器不会知道有问题,并且会很乐意编译您的代码。这段代码运行时会发生什么?异常(exception)?但是引入泛型正是为了解决类型安全问题。
关于协变和逆变的旁注
在 .NET 4.0 中,为委托(delegate)和接口(interface)添加了 generic covariance and contravariance 是正确的( 不是用于类 )。例如,
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
但这是唯一可能的,因为
IEnumerable<out T>
保证它 只返回 T
(关键字 out
)并且 从不接受 它。如果它接受 T
作为参数,它将为上述问题打开大门。因此,ICollection
不是协变的。以类似的方式,一些接口(interface)保证它们 只接受
T
(关键字 in
)并且 从不返回 它。此类接口(interface)称为逆变器,并允许分配给具有更具体 T
的变量:IComparer<Animal> animalComparer = // ...
IComparer<Dog> dogComparer = animalComparer; // comparer of animals is comparer of dogs