无法理解为什么无法编译对象初始化程序中的数组初始化程序
Can't understand why array initializer in object initializer cannot compiled
我看到了 and there was a update and https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#extension-add-methods-in-collection-initializers,但还是看不懂。
var arr = new int[] { 1, 2, 3 }
使用 stelem
生成 IL 代码。
但是 int[] arr = { 1, 2, 3 }
使用 RuntimeHelpers.InitializeArray
生成 IL 代码。我认为它正在使用那个答案中提到的 Array.Add
扩展方法。
但是在对象初始化器中,所有数组初始化器都会生成第二个代码。
举个例子,
new A() {
arr = new int[] { 1, 2, 3 }
}
数组 arr
也是使用 RuntimeHelpers.InitializingArray
创建的。那么,接下来的代码是不是就没有问题了?
new A() {
arr = { 1, 2, 3 } // Compiler error!
}
不像旧版本的 c# 编译器,它使编译器错误提示 system.array does not contain a definition for Add
。发生了什么?
EDIT
我认为没有 new []
的语法会产生差异,但实际上超过三个元素会产生不同的 IL 代码。
第二种语法 ({ arr = {... } }
) 是 value.arr.Add(.)
序列的语法糖 - 可能 value.arr
未在您的构造函数中初始化,因此 NRE。
假设 A
是:
class A {
public int[] arr {get;set}
}
然后
var x = new A() {
arr = { 1, 2, 3 } // Compiler error!
};
与
相同
var x = new A(); // note x.arr is default null here
x.arr.Add(1); // NRE is arr is list, can't compile for int[]
x.arr.Add(2);
x.arr.Add(3);
修复:使用列表,没有合理的方法向数组添加元素。
如果使用其他一些没有 .Add
的类型 - 实现该代码可见的扩展方法。
为什么 new
的版本有效:
var x = new A() {
arr = new int[] { 1, 2, 3 }
};
等同于 * 的
var x = new A();
x.arr = new int[] { 1, 2, 3 };
请注意,在数组初始值设定项中,您可以使用两种语法来达到相同的效果,但不能在数组字段的初始值设定项中使用 (All possible C# array initialization syntaxes)
int[] x = { 10, 20, 30 }; // valid, same as int[] x = new int[]{ 10, 20, 30 };
而 new A { arr = { 10, 20, 30} }
与 new A { arr = new int[3]{ 10, 20, 30} }
不同
*当必须观察到变化时,满足规则确实有点复杂 - 请参阅 Eric Lippert 的
在表达式上下文中,这些是合法的数组值:
new int[3] { 10, 20, 30 }
new int[] { 10, 20, 30 }
new[] { 10, 20, 30 }
在局部或成员变量初始值设定项上下文中,这是一个合法的数组初始值设定项:
int[] x = { 10, 20, 30 };
这是一个合法的集合初始化程序:
List<int> x = new List<int> { 10, 20, 30 };
而且,如果 X<T>
是 IEnumerable<T>
并且有一个方法 Add(T, T, T)
,这是合法的:
X<int> x = new X<int> { { 10, 20, 30}, {40, 50, 60} };
但是在成员或集合初始化器上下文中这是不是合法数组属性初始化器:
new A() {
arr = { 10, 20, 30 }
}
(注意我这里对数组的规则进行了总结和注释)
这个问题,据我理解是"why not?"
答案是"no good reason"。这只是 C# 语法以及对象和集合初始值设定项规则的一个奇怪之处。
我多次考虑解决这个问题,但总有更好的事情可以打发我的时间;这是一个基本上没有人受益的修复程序,因为解决方法非常简单。
我猜想没有什么能阻止 C# 团队设计、指定、实施、测试和发布该功能,除了事实上还有大约一百万个其他功能可以更好地利用他们的时间。
如果您对此有强烈的感觉,那么,编译器是开源的;随时提出该功能并提倡它。或者,就此而言,实施它并提交拉取请求。 (在您提出该功能后。)
在实现您想要的功能之前,您只需使用上面列出的三种 "expression" 形式之一。这样做并不累赘。
我看到了
var arr = new int[] { 1, 2, 3 }
使用 stelem
生成 IL 代码。
但是 int[] arr = { 1, 2, 3 }
使用 RuntimeHelpers.InitializeArray
生成 IL 代码。我认为它正在使用那个答案中提到的 Array.Add
扩展方法。
但是在对象初始化器中,所有数组初始化器都会生成第二个代码。 举个例子,
new A() {
arr = new int[] { 1, 2, 3 }
}
数组 arr
也是使用 RuntimeHelpers.InitializingArray
创建的。那么,接下来的代码是不是就没有问题了?
new A() {
arr = { 1, 2, 3 } // Compiler error!
}
不像旧版本的 c# 编译器,它使编译器错误提示 system.array does not contain a definition for Add
。发生了什么?
EDIT
我认为没有 new []
的语法会产生差异,但实际上超过三个元素会产生不同的 IL 代码。
第二种语法 ({ arr = {... } }
) 是 value.arr.Add(.)
序列的语法糖 - 可能 value.arr
未在您的构造函数中初始化,因此 NRE。
假设 A
是:
class A {
public int[] arr {get;set}
}
然后
var x = new A() {
arr = { 1, 2, 3 } // Compiler error!
};
与
相同var x = new A(); // note x.arr is default null here
x.arr.Add(1); // NRE is arr is list, can't compile for int[]
x.arr.Add(2);
x.arr.Add(3);
修复:使用列表,没有合理的方法向数组添加元素。
如果使用其他一些没有 .Add
的类型 - 实现该代码可见的扩展方法。
为什么 new
的版本有效:
var x = new A() {
arr = new int[] { 1, 2, 3 }
};
等同于 * 的
var x = new A();
x.arr = new int[] { 1, 2, 3 };
请注意,在数组初始值设定项中,您可以使用两种语法来达到相同的效果,但不能在数组字段的初始值设定项中使用 (All possible C# array initialization syntaxes)
int[] x = { 10, 20, 30 }; // valid, same as int[] x = new int[]{ 10, 20, 30 };
而 new A { arr = { 10, 20, 30} }
与 new A { arr = new int[3]{ 10, 20, 30} }
*当必须观察到变化时,满足规则确实有点复杂 - 请参阅 Eric Lippert 的
在表达式上下文中,这些是合法的数组值:
new int[3] { 10, 20, 30 }
new int[] { 10, 20, 30 }
new[] { 10, 20, 30 }
在局部或成员变量初始值设定项上下文中,这是一个合法的数组初始值设定项:
int[] x = { 10, 20, 30 };
这是一个合法的集合初始化程序:
List<int> x = new List<int> { 10, 20, 30 };
而且,如果 X<T>
是 IEnumerable<T>
并且有一个方法 Add(T, T, T)
,这是合法的:
X<int> x = new X<int> { { 10, 20, 30}, {40, 50, 60} };
但是在成员或集合初始化器上下文中这是不是合法数组属性初始化器:
new A() {
arr = { 10, 20, 30 }
}
(注意我这里对数组的规则进行了总结和注释)
这个问题,据我理解是"why not?"
答案是"no good reason"。这只是 C# 语法以及对象和集合初始值设定项规则的一个奇怪之处。
我多次考虑解决这个问题,但总有更好的事情可以打发我的时间;这是一个基本上没有人受益的修复程序,因为解决方法非常简单。
我猜想没有什么能阻止 C# 团队设计、指定、实施、测试和发布该功能,除了事实上还有大约一百万个其他功能可以更好地利用他们的时间。
如果您对此有强烈的感觉,那么,编译器是开源的;随时提出该功能并提倡它。或者,就此而言,实施它并提交拉取请求。 (在您提出该功能后。)
在实现您想要的功能之前,您只需使用上面列出的三种 "expression" 形式之一。这样做并不累赘。