Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么C#在两个int数组语法上表现不同_C#_Arrays - Fatal编程技术网

为什么C#在两个int数组语法上表现不同

为什么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 =

C#中的数组是引用类型上的隐式协变量:

但不是在值类型上,因此如果将
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};