在C#中创建新对象时,{}是否像()一样?

时间:2015-01-31 17:31:35

标签: c# .net instantiation

我刚才注意到在构造对象时使用{}代替()会得到相同的结果。

class Customer
{
    public string name;
    public string ID {get; set;}
}

static void Main()
{  
    Customer c1= new Customer{}; //Is this a constructor? 
    Customer c2= new Customer();

    //what is the concept behind the ability to assign values for properties 
    //and fields inside the {} and is not allowable to do it inside () 
    //without defining a constructor:

    Customer c3= new Customer{name= "John", ID="ABC"};
}

在C#中创建新对象时{}是否像()一样?

7 个答案:

答案 0 :(得分:107)

在C#中有三种直接创建新对象的方法:

  • 带参数列表的简单构造函数调用:

    new Foo()       // Empty argument list
    new Foo(10, 20) // Passing arguments
    
  • 带参数列表的对象初始值设定项

    new Foo() { Name = "x" }       // Empty argument list
    new Foo(10, 20) { Name = "x" } // Two arguments
    
  • 没有参数列表的对象初始值设定项

    new Foo { Name = "x" }
    

最后一种形式是完全等效以指定一个空参数列表。通常它会调用无参数构造函数,但它可以调用一个构造函数,其中所有参数都有默认值。

现在在我给出的两个对象初始值设定示例中,我设置了Name属性 - 您可以设置其他属性/字段,甚至可以设置 no 属性和字段。所以这三个都是等价的,实际上没有传递构造函数参数并且没有指定要设置的属性/字段:

new Foo()
new Foo() {}
new Foo {}

其中,第一个是最常规的。

答案 1 :(得分:48)

() - 调用无参数构造函数。

{} - 应该用于分配属性。

使用{}而不使用()是一种快捷方式,只要有无参数构造函数就可以使用。

答案 2 :(得分:5)

  

您可以使用对象初始值设定项以声明方式初始化类型对象,而无需显式调用该类型的构造函数。

https://msdn.microsoft.com/en-us/library/bb397680.aspx

如果类型具有默认的构造函数,也可以省略调用构造函数。所以

Customer c1 = new Customer { };

完全相同
Customer c1 = new Customer() { };

答案 3 :(得分:3)

Customer c1 = new Customer {};

这是一个空对象初始值设定项。根据{{​​3}},除非指定要使用的构造函数,否则对象初始值设定项将调用默认构造函数。由于没有进行初始化,因此将使用默认构造函数进行编译。

要回答这个问题,如果在{C#'中创建新对象时'{}行为[{1}},则我们需要详细介绍。对于您关心的所有内容,生成的对象将包含相同的数据,但生成的IL不相同。

以下示例

()

将编译为此IL(仅主要方法,不进行优化的调试)

namespace SO28254462
{
    class Program
    {
        class Customer
        {
            private readonly Foo foo = new Foo();

            public string name;

            public Customer()
            {
            }

            public Customer(string id)
            {
                this.ID = id;
            }

            public string ID { get; set; }

            public Foo Foo
            {
                get
                {
                    return this.foo;
                }
            }
        }

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

        static void Main(string[] args)
        {
            Customer c1 = new Customer { };

            Customer c2 = new Customer();

            Customer c3 = new Customer { name = "John", ID = "ABC", Foo = { Bar = "whatever" } };

            Customer c4 = new Customer();
            c4.name = "John";
            c4.ID = "ABC";
            c4.Foo.Bar = "whatever";

            Customer c5 = new Customer("ABC") { name = "John", Foo = { Bar = "whatever" } };

            Customer c6 = new Customer("ABC");
            c6.name = "John";
            c6.Foo.Bar = "whatever";
        }
    }
}

正如我们所看到的,前两个.method private hidebysig static void Main ( string[] args ) cil managed { // Method begins at RVA 0x2050 // Code size 201 (0xc9) .maxstack 2 .entrypoint .locals init ( [0] class SO28254462.Program/Customer c1, [1] class SO28254462.Program/Customer c2, [2] class SO28254462.Program/Customer c3, [3] class SO28254462.Program/Customer c4, [4] class SO28254462.Program/Customer c5, [5] class SO28254462.Program/Customer c6, [6] class SO28254462.Program/Customer '<>g__initLocal0', [7] class SO28254462.Program/Customer '<>g__initLocal1' ) IL_0000: nop IL_0001: newobj instance void SO28254462.Program/Customer::.ctor() IL_0006: stloc.0 IL_0007: newobj instance void SO28254462.Program/Customer::.ctor() IL_000c: stloc.1 IL_000d: newobj instance void SO28254462.Program/Customer::.ctor() IL_0012: stloc.s '<>g__initLocal0' IL_0014: ldloc.s '<>g__initLocal0' IL_0016: ldstr "John" IL_001b: stfld string SO28254462.Program/Customer::name IL_0020: ldloc.s '<>g__initLocal0' IL_0022: ldstr "ABC" IL_0027: callvirt instance void SO28254462.Program/Customer::set_ID(string) IL_002c: nop IL_002d: ldloc.s '<>g__initLocal0' IL_002f: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo() IL_0034: ldstr "whatever" IL_0039: callvirt instance void SO28254462.Program/Foo::set_Bar(string) IL_003e: nop IL_003f: ldloc.s '<>g__initLocal0' IL_0041: stloc.2 IL_0042: newobj instance void SO28254462.Program/Customer::.ctor() IL_0047: stloc.3 IL_0048: ldloc.3 IL_0049: ldstr "John" IL_004e: stfld string SO28254462.Program/Customer::name IL_0053: ldloc.3 IL_0054: ldstr "ABC" IL_0059: callvirt instance void SO28254462.Program/Customer::set_ID(string) IL_005e: nop IL_005f: ldloc.3 IL_0060: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo() IL_0065: ldstr "whatever" IL_006a: callvirt instance void SO28254462.Program/Foo::set_Bar(string) IL_006f: nop IL_0070: ldstr "ABC" IL_0075: newobj instance void SO28254462.Program/Customer::.ctor(string) IL_007a: stloc.s '<>g__initLocal1' IL_007c: ldloc.s '<>g__initLocal1' IL_007e: ldstr "John" IL_0083: stfld string SO28254462.Program/Customer::name IL_0088: ldloc.s '<>g__initLocal1' IL_008a: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo() IL_008f: ldstr "whatever" IL_0094: callvirt instance void SO28254462.Program/Foo::set_Bar(string) IL_0099: nop IL_009a: ldloc.s '<>g__initLocal1' IL_009c: stloc.s c5 IL_009e: ldstr "ABC" IL_00a3: newobj instance void SO28254462.Program/Customer::.ctor(string) IL_00a8: stloc.s c6 IL_00aa: ldloc.s c6 IL_00ac: ldstr "John" IL_00b1: stfld string SO28254462.Program/Customer::name IL_00b6: ldloc.s c6 IL_00b8: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo() IL_00bd: ldstr "whatever" IL_00c2: callvirt instance void SO28254462.Program/Foo::set_Bar(string) IL_00c7: nop IL_00c8: ret } // end of method Program::Main 初始化已经转换为相同的IL(IL_0001到IL_000c)。之后,它变得更有趣:从IL_000d到IL_0041,我们看到创建了一个新对象并将其分配给不可见的临时变量Customer,并且只有在完成之后,结果值才会分配给{{1} }。这是C#编译器实现对象初始化程序的方法。设置公共属性&#34;手动&#34;在查看如何将<>g__initLocal0从IL_0042初始化为IL_006a时,实例化对象很明显 - 没有临时变量。

IL_0070直到结尾等同于前面的示例,除非它们将非默认构造函数与对象初始值设定项结合使用。正如您所料,它只是以与以前相同的方式编译,但使用指定的构造函数。

长话短说:从C#开发者的角度来看,结果基本相同。对象初始化器是简单的语法糖和编译器技巧,可以帮助使代码更容易阅读。但是,对于FWIW,生成的IL与属性的手动初始化不同。尽管如此,编译器发出的不可见临时变量的成本在几乎所有C#程序中都是微不足道的。

答案 4 :(得分:2)

没有新版本的C#隐式创建用于对象初始化的构造函数

Customer c1= new Customer{};

以上与

相同
Customer c1= new Customer()
{
};

Object and Collection Initializers (C# Programming Guide)

答案 5 :(得分:1)

Customer c1= new Customer{} - 这是属性的初始化器。你可以把它写成:

Customer c1 = new Customer{
              name="some text",
              ID="some id"
              };

答案 6 :(得分:1)

在您的特定情况下,是的,它会产生相同的结果,但不是您可能认为的原因。

要了解结果,请说你有这样的课程:

class Customer
{
    public string name;
    public string ID {get; set;}

    public Customer() 
    { 
    }

    public Customer(string n, string id)
    {
        name = n;
        ID = id;
    }
}

当您按照以下方式创建时:

Customer c = new Customer("john", "someID");

你调用第二个构造函数并告诉类你传递这些值并负责执行它认为构造函数的最佳谎言(在这种情况下,它将使用这些值传递他们到公共领域)。

当您按照以下方式创建时:

Customer c = new Customer { name = "john", ID = "someID" };

您自己设置公共字段并使用空构造函数。

无论哪种方式,您都应该避免使用公共字段,因为它不安全。你不应该让外面的任何人像这样直接修改它们。而是仅使用私有字段并使用公共属性来管理来自外部的访问!