代码之家  ›  专栏  ›  技术社区  ›  20chan

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

  •  1
  • 20chan  · 技术社区  · 8 年前

    我看到了 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 } 使用生成IL代码 stelem . 但是 int[] arr = { 1, 2, 3 } 使用生成IL代码 RuntimeHelpers.InitializeArray . 我认为它正在使用 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 回复  |  直到 8 年前
        1
  •  3
  •   Alexei Levenkov    8 年前

    第二种语法( { 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} }


    * 当必须观察到变化时,满足规则确实有点复杂-参见埃里克·利珀特的 comment below

        2
  •  3
  •   Eric Lippert    8 年前

    表示 上下文这些是合法的数组值:

    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 )

    我的理解是,问题是“为什么不呢?”

    答案是“没有好的理由”。这只是C#语法以及对象和集合初始值设定项规则的一个奇怪之处。

    我曾多次考虑解决这个奇怪的问题,但总有更好的办法来利用我的时间;这是一个基本上对任何人都没有好处的修复,因为解决方法非常简单。

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

    如果您对此有强烈的感觉,那么,编译器是开源的;您可以自由地提出该功能并为其辩护。或者,就此而言,实现它并提交一个请求。( 之后 您可以提出该功能。)

    在实现您想要的功能之前,您只需使用上面列出的三种“表达式”形式之一。这样做并不累赘。