我正在从this博客和
中阅读有关协方差和相反性的信息。关于数组的协方差让我感到困惑
现在,如果我有这个
object[] obj= new string[5];
obj[0]=4;
为什么在运行时出现错误?理论上obj是Object类型的变量,并且Object可以存储任何类型,因为所有类型都继承自Object类。现在,当我运行此代码时,我没有遇到任何运行时错误,有人可以向我解释为什么
class baseclass
{
}
class client
{
static void Main()
{
object obj = new baseclass();
obj = 4;
Console.Read();
}
}
答案 0 :(得分:8)
这实际上令人困惑。
您说InputBuffer
时,object[] objs = new string[4] {};
实际上是一个字符串数组。不安全的数组协方差是不安全的,因为类型系统在骗你。这就是为什么它不安全的原因。您认为您的数组可以容纳一个装箱的整数,但是它实际上是一个字符串数组,除了字符串之外,它实际上什么也不能容纳。
您的问题是“为什么不安全”,然后举一个为什么不安全的例子。这是不安全的,因为当您执行看起来应该安全的操作时,它会在运行时崩溃。这违反了类型系统的最基本规则:变量实际上包含变量类型的值。
对于类型objs
的变量,这不是谎言。您可以在该变量中存储任何对象,因此很安全。但是类型object
的变量是一个谎言。您可以将内容存储在不是object[]
的变量中。
在我看来,这是C#和CLR最糟糕的功能。 C#具有此功能,因为CLR具有此功能。 CLR之所以拥有它,是因为Java具有它,并且CLR设计者希望能够在CLR中实现类似Java的语言。我不知道为什么Java有它。这是一个可怕的想法,他们不应该这样做。
现在清楚了吗?
答案 1 :(得分:0)
数组类型为T[]
的对象具有三个显着的功能:
从数组中读取的任何值都可以存储在T
类型的容器中。
从数组中读取的任何值都可以存储回 same 数组中。
适合T
类型的容器的任何值都可以存储到数组中。
类型为T[]
的非空引用将能够保存对类型为U[]
的任何对象的引用,其中U
源自T
。对于从U
派生的任何可能的类型T
,从U[]
读取的任何值都可以存储到类型T
的容器中,也可以存储回相同的类型数组。如果类型为T
的容器持有对从T
派生但不是U
类型或不是从U
派生的任何类型的对象的引用,则{ {1}}将无法保存该引用。
允许代码从一个数组中读取一项并将其写回到同一数组,而又不允许它要求从一个数组中读取一项并将其写入另一个数组,这很尴尬。 C#并未尝试通过编译时约束来限制此类操作,而是选择说,如果代码尝试将U[]
中保存的值存储到通过T
标识的数组中,则该操作将成功如果T[]
为null或标识的类型不是从T
所标识的实际数组的元素类型派生的对象。