C# Collection Initializer 在对象初始值设定项中不被认可

C# Collection Initializer not honored in an object initializer

为什么对象初始化器不支持集合初始化器?

   public class Foo
   {
        public string[] Bar { get; set; }

        public Foo() { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var foo = new Foo()
            {
                Bar = { }
            };

            Assert.Null(foo.Bar); // Null not empty...what gives?

        }
    }

期望底层数组将设置为初始化的空集合。

因为default(string[])null。您必须对其进行初始化,并且可以为其使用构造函数:

public class Foo
{
   public string[] Bar { get; set; }
   public Foo() 
   {
       Bar = new string[0];
   }
}

在这种情况下,它将在 Bar 属性 上实例化一个新的空数组。

如果集合初始化器用作成员初始化器的一部分,则不会实例化新集合。它只是在集合上调用 Add(...) 方法的语法糖。

请注意,在这个(稍作修改的)示例中,抛出了一个 NullReferenceException

public class Foo
{
    public List<string> Bar { get; set; }
}

void Main()
{
    var foo = new Foo()
    {
        Bar = { "hello" }
    };
}

C# 规范在第 7.6.10.2 节中定义了此行为:

A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property.

这种初始化新集合的方式在内部被重写为每个元素的 Bar.Add(),并且由于您没有向其中添加任何内容,因此不会生成对 .Add() 的任何调用。

如果你反编译它看起来像这样(注意初始化是如何丢失的)。

private static void Main(string[] args)
{
    new Foo();
}

您可以尝试在初始化中实际添加一个字符串。它不会编译,但会显示以下错误。

var foo = new Foo()
{
    Bar = { "" }
};
error CS1061: 'string[]' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string[]' could be found (are you missing a using directive or an assembly reference?)

现在如果你用一个新数组初始化它,它当然会工作。

var foo = new Foo()
{
    Bar = new[] { "" }
};

检查这个 dempiled 会产生这个。

private static void Main(string[] args)
{
    Foo foo = new Foo();
    string[] array = new string[1];
    array[0] = "";
    foo.Bar = array;
}

简而言之,为了安全起见,请在 class 构造函数中初始化数组。

我不确定你为什么要这样声明一个数组,你可以按照@Felipe Oriani 提到的那样做,但我假设你知道这一点,你只是想了解为什么你的方法没有像它应该工作的那样工作。所以我将尝试与此相关,而不是声明数组的最佳实践。

这是一个有趣的问题,因为我在试图理解它发生的原因时发现了一些奇怪的事情。

我注意到如果我在构造函数中用大括号初始化它,它不会编译 (invalid expression term '{')。不确定为什么它不能编译而其他方法可以编译?!

public Foo()
{
    Bar = { };
}

然后我提到如果我不在对象内声明它,它会很好地工作:

string[] bar = { }; // Creates an empty array of strings

所以这样做是可能的,而且它会如您所愿地工作:

public class Foo
{
    public string[] Bar { get; set; }

    public Foo(string[] bar)
    {
        Bar = bar;
    }
}

class Program
{
    static void Main(string[] args)
    {
        string[] bar = { };
        var foo = new Foo(bar); // foo.Bar is not empty string array
    }
}