无法理解为什么对象初始值设定项中的数组初始值设定项无法编译

时间:2018-01-26 17:09:34

标签: c# clr

我看到NRE when using array initializer in object initializer并且有更新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代码。

2 个答案:

答案 0 :(得分:3)

第二种语法(block do ... end)是{ arr = {... } }序列的语法糖 - 可能value.arr.Add(.)未在构造函数中初始化,因此也就是NRE。

假设value.arr是:

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 } 
};

请注意,在数组初始值设定项中,您可以将两种语法用于相同的效果,但不能在数组字段的初始值设定项中使用(All possible C# array initialization syntaxes

var x = new A();
x.arr = new int[] { 1, 2, 3 };

int[] x = { 10, 20, 30 }; // valid, same as int[] x = new int[]{ 10, 20, 30 }; 与<{1}} 相同

* 当必须观察变化时,满足规则真的有点复杂 - 参见Eric Lippert的comment below

答案 1 :(得分:3)

表达式上下文中,这些是合法的数组值:

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 }
}

(请注意,我在这里总结并评论了数组规则https://stackoverflow.com/a/5678393/88656

正如我所理解的那样,问题是&#34;为什么不呢?&#34;

答案是&#34;没有充分理由&#34;。它只是C#语法的奇怪之处以及对象和集合初始化器的规则。

我多次考虑修复这种奇怪之处,但总有一些事情与我的时间有关;它是一个基本上没有人受益的解决方案,因为解决方法非常简单。

我猜想没有什么能阻止C#团队设计,指定,实施,测试和发布该功能,除了还有大约一百万个其他功能可以更好地利用他们的时间

如果您对此感到强烈,那么编译器就是开源的;随意提出这个功能并提倡它。或者,就此而言,实施它并提交拉取请求。 (之后提出功能。)

在实现您想要的功能之前,您只需使用三个&#34;表达式中的一个&#34;上面列出的表格。这样做并不累赘。