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

Mic*_*l B 14 c# arrays

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

string[] strings = new []{"Hey there"};
object[] objects = strings;
objects[0] = new object();
Run Code Online (Sandbox Code Playgroud)

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

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

IList版本

string[] strings = new []{"Hey there"};
IList objects = strings;
objects[0] = new object();
Run Code Online (Sandbox Code Playgroud)

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

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

Eri*_*ert 24

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

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

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

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

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

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

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

这是一个奇怪的问题.问题基本上是"我已经有两把枪,我可以用脚射击自己了,所以为什么用三分之一将自己射到脚上对我有害?"

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

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

  • @Joan:我绝不预见到这一点.我说的是十年后见之明的好处. (3认同)

Jon*_*eet 14

是的,IListArray允许你犯同样的错误 - 因为它们是弱类型的API开始.

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