为什么C#在两个int数组语法上表现不同
C#中的数组是引用类型上的隐式协变量: 但不是在值类型上,因此如果将为什么C#在两个int数组语法上表现不同,c#,arrays,C#,Arrays,C#中的数组是引用类型上的隐式协变量: 但不是在值类型上,因此如果将string更改为int,则会出现编译错误: object[] listInt = new int[] {0, 1}; // compile error 现在,需要注意的是,当您声明int数组时,下面的两个语法不会显式声明类型int,只需在new[]上进行区分,编译器就会区别对待: object[] list1 = { 0, 1 }; //compile successfully object[] list2 =
string
更改为int
,则会出现编译错误:
object[] listInt = new int[] {0, 1}; // compile error
现在,需要注意的是,当您声明int
数组时,下面的两个语法不会显式声明类型int
,只需在new[]
上进行区分,编译器就会区别对待:
object[] list1 = { 0, 1 }; //compile successfully
object[] list2 = new[] {0, 1}; //compile error
您将获得object[]list1={0,1}代码>编译成功,但object[]list2=new[]{0,1}代码>编译错误
似乎C#编译器
object[] list1 = { 0, 1 };
作为
但是
作为
为什么C#编译器在这种情况下的行为方式不同?当您使用{
和}
时,您使用集合初始值设定项(请参阅:)。这些括号之间的值必须放在某个地方。因此,必须创建一个集合。编译器将对上下文进行分析,找出哪种类型的集合
在第一种情况下:object[]list1={0,1}代码>显然应该创建一个集合。但是它应该是什么样的呢?某个地方没有新的操作。只有一个提示:list1
属于object[]
类型。因此编译器创建该集合并用valius填充它
在第二个示例中,object[]list1=new[]{0,1}代码>还有另一个提示:new[]
。这个提示明确地说:将有一个数组。该数组没有类型,因此它将尝试通过分析值来查找数组的类型。这些都是int
,因此它将创建一个int
数组并填充它。另一个提示object[]
被完全忽略,因为创建的提示比它应该分配到的位置的提示重要得多。现在编译器想要将这个数组分配给list1和BOOM:这不合适 数组初始化器是一种方便的编译器。如果我说“我正在声明一个对象数组并给它赋值”,那么编译器可以合理地假设您的{0,1}
是一个对象数组并将其解释为对象数组。虽然语法看起来是赋值,但事实并非如此:您使用的是初始化器。这种语法的缩写是object[]list1=newobject[]{0,1}
当您说new[]{0,1}
时,这是一个创建数组并初始化它的表达式。此表达式的计算独立于您将其分配给的对象,并且由于编译器检测到隐式整数类型,因此它会创建一个int[]
。该表达式的正手版本是object[]list2=newint[]{0,1}
如果比较这两个语句的直接版本,就可以清楚地看到它们的不同之处。声明
object[] listInt = new int[] {0, 1};
object[] listInt = new string[] {"0", "1"};
无效,因为值类型不允许协变数组转换(并且int
是值类型)。或者,宣言
object[] listInt = new int[] {0, 1};
object[] listInt = new string[] {"0", "1"};
有效,因为引用类型允许协变数组转换。这是因为赋值x=(object)myString
只涉及简单赋值,但y=(object)myInt
需要装箱操作
现在来看看这两个声明之间的区别。在声明object[]list2=new[]{0,1}
中,由于类型推断的工作方式,它首先查看右侧表达式,并得出结论认为new[]{0,1}
应被视为new int[]{0,1}
。然后,它尝试将这个int数组分配给一个对象数组,由于值类型的协变转换问题而给出一个错误。但是,声明对象[]list1={0,1}
使用集合初始值设定项,在这些情况下,集合的类型是定义类型的位置,因此每个元素将转换为集合所期望的类型。编译的版本使用数组初始值设定项初始化list1
。C语言规范第1.110节(“数组初始值设定项”)规定:
数组初始值设定项由一系列变量初始值设定项组成,
由“{”和“}”标记包围,并由“,”标记分隔。每个
变量初始值设定项是一个表达式,如果是
多维数组,嵌套数组初始值设定项
语境
使用哪个数组初始值设定项决定数组的类型
正在初始化。在数组创建表达式中,数组类型
紧跟在初始值设定项之前,或从
数组初始值设定项中的表达式。在字段或变量中
声明时,数组类型是要创建的字段或变量的类型
声明
在字段或变量中使用数组初始值设定项时
声明,例如:
int[] a = {0, 2, 4, 6, 8};
它只是等效数组创建表达式的简写:
int[] a = new int[] {0, 2, 4, 6, 8};
所以很明显,这应该是编译的
第二个版本使用一个显式数组创建表达式,在该表达式中,您可以明确指示编译器要创建什么类型的数组。§1.51.10.4(“阵列创建表达式”)规定:
第三种形式的数组创建表达式称为
隐式类型数组创建表达式。它类似于
第二种形式,除了数组的元素类型不是
明确给出,但确定为最佳通用类型(§1.50.2.14)
数组初始值设定项中表达式集的
因此,第二个版本相当于
object[] list2 = new int[] { 0, 1 };
因此,现在的问题实际上变成了“为什么我不能将int[]
分配给对象[]
”,正如您在问题末尾提到的那样。答案也很简单,见§1.109(“阵列协方差”):
数组协方差特别不扩展到
值类型。例如,不存在允许<
int[] a = new int[] {0, 2, 4, 6, 8};
object[] list2 = new int[] { 0, 1 };
object[] listInt = new int[] {0, 1};
object[] listInt;
listInt = new int[] {0, 1};