为什么arrays协方差被认为是如此可怕?

在.NET中,引用类型数组是共变体。 这被认为是一个错误。 但是,我不明白为什么这么糟糕考虑以下代码:

string[] strings = new []{"Hey there"}; object[] objects = strings; objects[0] = new object(); 

哦,这个编译并且会在运行时失败。 当我们试图将一个对象粘贴到一个字符串[]中时。 好吧,我同意臭,但是T []扩展了Array并且还实现了IList (和IList ,我想知道它是否实现了IList …>。数组和IList都让我们同样可怕错误。

 string[] strings = new []{"Hey there"}; Array objects = strings; objects.SetValue(new object(),new[]{0}); 

IList版本

 string[] strings = new []{"Hey there"}; IList objects = strings; objects[0] = new object(); 

T []类由CLR生成,并且必须包含对set_Item方法的等效类型检查(数组实际上没有一个)。

是否担心设置为T []必须在运行时进行类型检查(这违反了编译时期望的类型安全性)? 当有相同的方法通过上面提供的手段射击自己的脚时,为什么它被认为有害于arrays显示这种属性?

是的, IListArray允许你犯同样的错误 – 因为它们是弱类型的API。

数组看起来像是强类型的(在编译时),但实际上它们不是。 它们可以很容易地安全(并且更快),但它们不是。 这只是性能和编译时安全性的浪费机会:(

在.NET中,引用类型数组是共变体。 这被认为是一个错误。

某些人认为类型安全破坏arrays协方差是.NET设计中的一个错误。 所有人都不这么认为。 我不认为这是一个错误 ; 我认为这是一个不幸的选择。 所有设计过程都涉及不合需要的选择。 在这种情况下,选择是添加一个不安全的隐式转换,在所有数组写入上施加运行时成本,或者构建一个无法轻松实现Java类型系统的类型系统。 这是一个艰难的选择,类型系统的设计者用他们拥有的信息做出了最好的选择。

当然,这种解释仅仅是一个问题; 那不就是Java的设计师犯了错误的情况吗? 可能是的,可能没有; 可能Java的设计者也在他们的类型系统的设计中面临权衡。 任何关于Java类型系统开发历史的专家谁愿意在这些权衡中讨论,我有兴趣知道。

如果.NET类型系统的设计者选择避开安全破坏arrays协方差,那么,凭借十年后见之明,我个人会更喜欢它。 但这并不能使这种选择成为“错误”,只是让它有点不幸。

是否担心设置为T []必须在运行时进行类型检查(这违反了编译时期望的类型安全性)?

是。 这意味着看起来应该总是成功运行的代码可能在运行时失败。 这意味着正确的代码会对其施加性能损失。

当有相同的方法通过上面提供的手段射击自己的脚时,为什么它被认为有害于arrays显示这种属性?

这是一个奇怪的问题。 问题基本上是“我已经有两把枪,我可以用脚射击自己了,所以为什么在第三次用脚射击自己有害于我?”

存在两种违反类型安全的危险模式并不会使第三种这种模式的危险性降低。

当你绝对肯定地知道你正在做的事情是安全的时候,那些违反类型安全的语言和运行时function就存在,即使编译器不知道它也是如此。 如果您不能很好地理解这些function以便安全使用它们,请不要使用它们。

我认为你关于IList指出了值得考虑的事情。

IList由数组实现非常有用。 实现它的其他集合也很有用。

现在,在过去的5年中,我们经常发现处理IList更有用(或同样有用且更安全)。

在.NET2.0之前,我们没有IList ,我们只有IList 。 很多情况下,人们可能会在数组和其他集合之间移动,其中generics(最好)在generics之前,在许多情况下现在让我们更有信心地在类型集合和类型数组之间移动。

因此,在做出相关决策时,支持协变arrays的论据比现在更大。 而且,当它没有generics时,他们在Java中建立类似的决策只会增加这个事实。

arrays方差的一个代价是对非密封参考类型的数组的赋值有点贵。 但考虑到参考类型的分配已经与GC有很大关系,我想这个成本并不重要。