【问题标题】:C# Collection Initializer not honored in an object initializer [duplicate]C# Collection Initializer 在对象初始化程序中不被尊重[重复]
【发布时间】:2021-09-10 16:58:05
【问题描述】:

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

   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?

        }
    }

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

【问题讨论】:

    标签: c# arrays initialization overriding braces


    【解决方案1】:

    对于每个元素,初始化新集合的这种方式在内部重写为 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[] { "" }
    };
    

    检查这个 depiled 会产生这个。

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

    简而言之,在你的类构造函数中初始化你的数组是安全的。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

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

        public class Foo
        {
            public List<string> Bar { get; set; }
        }
        
        void Main()
        {
            var foo = new Foo()
            {
                Bar = { "hello" }
            };
        }
        

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

        在等号之后指定集合初始值设定项的成员初始值设定项是嵌入式集合的初始化。 不会将新集合分配给字段或属性,而是将初始化程序中给出的元素添加到字段或属性引用的集合中。

        【讨论】:

          【解决方案4】:

          我不确定你为什么要以这种方式声明一个数组,你可能会做@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
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2015-12-03
            • 1970-01-01
            • 1970-01-01
            • 2023-03-17
            • 2013-07-23
            • 2012-01-17
            • 2016-02-26
            • 1970-01-01
            • 2011-10-09
            相关资源
            最近更新 更多