{}在C#中创build新对象时的行为如何()

我只注意到,使用{}而不是()在构造对象时会得到相同的结果。

 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#中创build新对象时的行为如何()

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

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

     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" } 

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

现在,在我给出的两个对象初始值设定项例子中,我已经设置了Name属性 – 并且可以设置其他属性/域,甚至不设置属性和域。 所以这三个都是等价的,实际上不传递任何构造参数,也没有指定任何属性/域来设置:

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

其中,第一个是最传统的。

() – 调用无参数的构造函数。

{} – 应该被用来分配属性。

使用{}()是一个快捷方式,只要有一个无参数的构造函数就会工作。

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

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

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

 Customer c1 = new Customer { }; 

是完全一样的

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

这是一个空的对象初始值设定项。 按照规范 ,对象初始值设定项会调用默认的构造函数,除非你指定了要使用的构造函数。 由于没有完成初始化,它将被编译为使用默认构造函数。

为了回答这个问题,如果在C#中创build一个新的对象时{}()那样操作,我们不得不进行更多的细节。 对于你所关心的所有,结果对象将包含相同的数据,但生成的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"; } } } 

将编译到这个IL(只有主要的方法,没有优化的Debug)

 .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 

正如我们所看到的,前两个Customer初始化已被转换为相同的IL(IL_0001到IL_000c)。 之后,它变得更有趣了:从IL_000d到IL_0041,我们看到创build了一个新对象,并将其分配给不可见的临时variables<>g__initLocal0并且只有在完成之后,才会将结果值赋给c3 。 这是C#编译器如何实现对象初始值设定项。 当你看看c4是如何从IL_0042初始化到IL_006a的时候,在实例化对象之后“手动”设置公共属性的区别是显而易见的 – 没有临时variables。

IL_0070直到最后都等同于前面的例子,除了它们使用一个非默认构造函数和对象初始值设定项的组合。 正如你可能期望的那样,它只是像以前一样编译,但是使用指定的构造函数。

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

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

 Customer c1= new Customer{}; 

以上是一样的

 Customer c1= new Customer() { }; 

对象和集合初始化器(C#编程指南)

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

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

在你的具体情况下,是的,它会产生一个相同的结果,但不是你可能会想的原因。

为了理解结果,假设你有这样的一个类:

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

当你像这样创build它时:

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

你调用第二个构造函数,并告诉你传递这些值的类,并负责做它认为它是构造函数中最好的谎言(在这种情况下,它将使用这些值将它们传递给公共字段)。

当你像这样创build它时:

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

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

无论如何,你应该避免使用公共领域,因为这是不安全的。 你不应该让外面的任何人直接修改它们。 而是只使用私人领域,并使用公共属性来pipe理外部访问!