【问题标题】:Object initializer syntax confusion. Using parentheses in initializer?对象初始值设定项语法混乱。在初始化程序中使用括号?
【发布时间】:2018-07-08 02:34:24
【问题描述】:

所以我在查看the documentation 以了解如何使用对象初始化器,无论是否用于匿名类型。我唯一想知道的是为什么(如果重要的话)示例中存在差异。

它是这样的:对于一只猫

class Cat
{
    public int Age { get; set; }
    public string Name { get; set; }
}

我可以看到第一个例子是这样的:

Cat cat = new Cat { Age = 10, Name = "Fluffy" };

这是有道理的。但是,您可以找到以下内容:

List<Cat> cats = new List<Cat>
{
    new Cat(){ Name = "Sylvester", Age=8 },
    new Cat(){ Name = "Whiskers", Age=2 }
};

现在我的问题是:(为什么)new Cat{...} 之间有区别 和new Cat(){...} ? 为什么要使用(或不使用)括号?

【问题讨论】:

  • 我希望这种语言的“优化”应该花一些时间来考虑优势之间的平衡——让你免于添加()——和劣势——它引起的混乱和大惊小怪加上一个新规则要记住,当类具有无参数构造函数时,您只能使用这种语法糖果。作为旁注,在 C++ 中它有一个目的:零初始化与不确定值。

标签: c# object-initializers


【解决方案1】:

如果一个对象有一个无参数构造函数,你可以省略括号。所以这两个都是有效的

// Assuming cat has a constructor with no parameters
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat cat = new Cat() { Age = 10, Name = "Fluffy" };

List&lt;T&gt; 本身有一个对象初始化器,您可以在其中提供任意数量的项目,它们会自动添加到集合中。

List<Cat> cats = new List<Cat>()
{
    new Cat(){ Name = "Sylvester", Age=8 }, // Add sylvester to the List
    new Cat(){ Name = "Whiskers", Age=2 } // And Whiskers too
};

如上所述,您也可以在此处删除括号

List<Cat> cats = new List<Cat>
{
    new Cat { Name = "Sylvester", Age=8 }, // Add sylvester to the List
    new Cat { Name = "Whiskers", Age=2 } // And Whiskers too
};

【讨论】:

  • 我认为您对无参数构造函数(名称)的引用非常重要,因为它用于在 EF 等不同库中指定错误消息
  • @Jamiec ,这似乎是我缺少的部分:如果一个对象有一个无参数构造函数,你可以省略括号
  • @CamiloTerevinto,您能否详细说明您刚才指出的内容?
  • @bgiuga 不带参数的构造函数称为无参数构造函数。知道名称很重要,因为有些库在抛出错误消息时会明确引用它们
【解决方案2】:

这些只是在 C# 中使用对象初始化来实例化对象的两种可接受的等效语法(如果您只是在做 var cat = new Cat();,则不能省略括号)。

  • 由于您的类没有显式构造函数,因此为 Cat 类提供了一个默认的无参数构造函数
  • new Cat {...} 允许实例化和初始化 Cat 对象的属性作为new Cat() {...} 的快捷方式,调用提到的构造函数。

关于构造函数的部分很重要。如果没有隐式默认构造函数/显式无参数构造函数,那么你不能省略括号,无论如何你都必须在其中提供参数:

public class Cat {
    public string Name;
    public int Age;

    public Cat(string s) { // since I provide a constructor with parameter here, no parameterless constructor exists
        Name = s; 
    } 
}

// ...
void TestCat() 
{

    // compilation error : 'Cat'' does not contain a constructor that takes 0 arguments
    //var badCat1 = new Cat { Name = "Felix", Age = 3} ;
    //var badCat2 = new Cat() { Name = "Felix", Age = 3} ;

    // works (but no way to remove parenthesis here, since there are parameters to pass to csontructor)
    var goodCat = new Cat("Felix") { Age = 3 } ;

    Console.WriteLine($"The cat {goodCat.Name} is {goodCat.Age} years old");
}

特殊情况:(在集合、列表、字典等方面经常使用...)。

如果一个类 T 实现了 IEnumerable(即有一个 IEnumerable GetEnumerator() 公共函数),并实现一个 Add 方法,然后对象初始化器将使用 Add 方法与集合进行枚举。

来自https://blog.mariusschulz.com/2014/06/26/fun-with-custom-c-collection-initializers的示例

创建特殊类“Points”,其作用类似于初始化时的“List”。

请注意,这也使用了现有的无参数构造函数!

public class Points : IEnumerable<Point3D>
{
    private readonly List<Point3D> _points;

    public Points()
    {
        _points = new List<Point3D>();
    }

    public void Add(double x, double y, double z)
    {
        _points.Add(new Point3D(x, y, z));
    }

    public IEnumerator<Point3D> GetEnumerator()
    {
        return _points.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

这样使用:

var cube = new Points
{
    { -1, -1, -1 },
    { -1, -1,  1 },
    { -1,  1, -1 },
    { -1,  1,  1 },
    {  1, -1, -1 },
    {  1, -1,  1 },
    {  1,  1, -1 },
    {  1,  1,  1 }
};

【讨论】:

  • 对于任何反对者,请解释这是怎么回事,以便我改进和/或删除我的答案。
  • 您可能想更详细地了解底部的问题:"(why) new Cat{...} 和 new Cat(){ 之间有区别吗...} ?我们为什么要使用(或不使用)括号?”
  • @ManfredRadlwimmer 感谢您的意见。我添加了更多细节,并尽量避免与其他已接受的答案过于冗长。
  • 赞成,因为提到了集合初始化器的幕后工作
猜你喜欢
  • 1970-01-01
  • 2022-10-04
  • 1970-01-01
  • 1970-01-01
  • 2015-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多