我的目标是 .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!
myListCat 的列表,您希望它如何处理 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

10-08 14:50