2010-11-30 35 views
13

在.NET引用类型数组中是协变量。这被认为是一个错误。不过,我不明白为什么这么糟糕请考虑下面的代码:为什么阵列协方差被认为如此可怕?

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

哦,这个编译并会在运行时失败。正如我们试图将一个对象粘贴到一个字符串[]中。好吧,我同意,很臭,但T []数组延伸,也实现了IList(和IList<T>,我不知道如果它实现IList<BaseType> ...>。这两个数组和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 []必须在运行时进行类型检查(这违反了编译时期望的类型安全性)?为什么在有相同的手段通过上面提供的手段在脚下射击自己时,阵列展示这种属性会有害吗?

+0

你是不是指对象[0] = new object(); ? – 2010-11-30 19:07:28

+0

是的,赶上! – 2010-11-30 19:09:52

回答

13

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

阵列看起来像他们强烈类型(在编译时),但实际上他们不是。他们可能如此容易安全(和更快),但他们不是。这只是一个性能和编译时安全性的浪费机会:(

+0

抖动不能优化出类型检查吗? – 2010-11-30 19:14:54

0

数组方差的一个代价是对非密封参考类型数组的赋值有点贵,但考虑到对引用类型的赋值已经做了很多相关的GC一点我想这成本不显著。

22

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

类型安全打破阵列协方差被认为是有些人是.NET设计中的一个错误。这不是所有人都这么认为的。我不认为它是一个错误;我认为这是一个不幸的选择。所有设计过程都涉及不良替代品之间的选择。在这种情况下,选择是在添加不安全的隐式转换之间进行,该转换会在所有阵列写入上施加运行时开销,或者构建无法轻松实现Java类型系统的类型系统。这是一个艰难的选择,类型系统的设计者根据他们所拥有的信息做出了最好的选择。

当然这个解释仅仅是问题乞求;那不就是Java的设计者犯了错误吗?可能是的,可能不是; Java的设计人员也可能在其类型系统的设计中面临折衷。任何关于Java类型系统开发历史的专家都想在这里介绍一下这些权衡之处,我很想知道。我认为,如果.NET类型系统的设计者选择避开安全破坏的数组协方差,那么我认为,如果有十年的事后认识,我个人会更喜欢它。但是这并不能使这个选择成为一个“错误”,这只会让它有点不幸。

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

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

为什么数组在展示这个属性的时候认为是有害的?当有相同的方法通过上面提供的方法在脚上拍摄自己?

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

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

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

1

我认为你的笔记IList指向这里值得考虑的事情。

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

现在,在过去的5年中,我们经常发现它更有用(或同样有用和更安全)来处理IList<T>。我们没有,我们只有IList。在很多情况下,可能会在数组和其他集合之间移动(在最好情况下)泛型之前,在许多情况下,我们可以更加自信地在类型化集合和类型化数组之间移动。

因此,在做出相关决定时,支持协变阵列的论据比现在更多。而当他们没有泛型的时候,他们在Java中做出类似的决定只会增加这个事实。