在 C# 中创建新对象时,{ } 的行为是否类似于 ( )?
Does { } act like ( ) when creating a new object in C#?
我刚刚注意到在构造对象时使用 {}
而不是 ()
会得到相同的结果。
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# 中创建新对象时,{}
是否像 ()
一样?
没有新版本的 C# 隐式创建用于对象初始化的构造函数
Customer c1= new Customer{};
以上同
Customer c1= new Customer()
{
};
Customer c1= new Customer{}
- 这是属性的初始值设定项。你可以写成:
Customer c1 = new Customer{
name="some text",
ID="some id"
};
You can use object initializers to initialize type objects in a declarative manner without explicitly invoking a constructor for the type.
https://msdn.microsoft.com/en-us/library/bb397680.aspx
如果类型具有默认构造函数,您也可以省略调用构造函数。所以
Customer c1 = new Customer { };
与
完全相同
Customer c1 = new Customer() { };
()
- 调用无参数构造函数。
{}
- 应该用于分配属性。
使用不带 ()
的 {}
是一种快捷方式,只要有无参数构造函数就可以使用。
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
属性 - 您可以设置其他 properties/fields,甚至设置 no 属性和字段。所以这三个都是等价的,实际上没有传递构造函数参数并且没有指定 properties/fields 来设置:
new Foo()
new Foo() {}
new Foo {}
其中,第一个是最常规的。
在您的特定情况下,是的,它会产生相同的结果,但不是出于您可能认为的原因。
为了理解结果,假设您有一个像这样的 class:
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");
您调用第二个构造函数并告诉 class 您正在传递这些值,并且执行它认为最好的任务的责任在于构造函数(在这种情况下,它将使用这些值来传递他们到 public 个字段)。
当你这样创建它时:
Customer c = new Customer { name = "john", ID = "someID" };
您自己设置了 public 字段,并且使用了空构造函数。
无论哪种方式,您都应该避免使用 public 字段,因为它不安全。你不应该让任何外部的人像这样直接修改它们。而是仅使用私有字段并使用 public 属性来管理来自外部的访问!
Customer c1 = new Customer {};
这是一个空的对象初始值设定项。根据 spec,对象初始值设定项将调用默认构造函数,除非您指定要使用的构造函数。由于没有进行初始化,所以会像使用默认构造函数一样编译。
要回答这个问题,如果“{}
在 C# 中创建新对象时表现得像 ()
”,我们必须更详细地说明。对于您关心的所有内容,生成的对象将包含相同的数据,但生成的 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(仅主要方法,不进行优化调试)
.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,我们看到创建了一个新对象并将其分配给不可见的临时变量 <>g__initLocal0
,只有在完成之后,结果值分配给 c3
。这就是 C# 编译器实现对象初始值设定项的方式。当您查看 c4
如何从 IL_0042 初始化为 IL_006a 时,在实例化对象后设置 public 属性 "manually" 的区别很明显 - 没有临时变量。
IL_0070 直到最后都等同于前面的示例,除了它们使用非默认构造函数和对象初始值设定项。如您所料,它只是按照与以前相同的方式进行编译,但使用指定的构造函数。
长话短说:从 C# 开发人员的角度来看,结果基本相同。对象初始值设定项是简单的语法糖和编译器技巧,有助于使代码更易于阅读。但是 FWIW,生成的 IL 与属性的手动初始化不同。尽管如此,编译器发出的不可见临时变量的成本在几乎所有 C# 程序中都应该是微不足道的。
我刚刚注意到在构造对象时使用 {}
而不是 ()
会得到相同的结果。
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# 中创建新对象时,{}
是否像 ()
一样?
没有新版本的 C# 隐式创建用于对象初始化的构造函数
Customer c1= new Customer{};
以上同
Customer c1= new Customer()
{
};
Customer c1= new Customer{}
- 这是属性的初始值设定项。你可以写成:
Customer c1 = new Customer{
name="some text",
ID="some id"
};
You can use object initializers to initialize type objects in a declarative manner without explicitly invoking a constructor for the type.
https://msdn.microsoft.com/en-us/library/bb397680.aspx
如果类型具有默认构造函数,您也可以省略调用构造函数。所以
Customer c1 = new Customer { };
与
完全相同Customer c1 = new Customer() { };
()
- 调用无参数构造函数。
{}
- 应该用于分配属性。
使用不带 ()
的 {}
是一种快捷方式,只要有无参数构造函数就可以使用。
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
属性 - 您可以设置其他 properties/fields,甚至设置 no 属性和字段。所以这三个都是等价的,实际上没有传递构造函数参数并且没有指定 properties/fields 来设置:
new Foo()
new Foo() {}
new Foo {}
其中,第一个是最常规的。
在您的特定情况下,是的,它会产生相同的结果,但不是出于您可能认为的原因。
为了理解结果,假设您有一个像这样的 class:
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");
您调用第二个构造函数并告诉 class 您正在传递这些值,并且执行它认为最好的任务的责任在于构造函数(在这种情况下,它将使用这些值来传递他们到 public 个字段)。
当你这样创建它时:
Customer c = new Customer { name = "john", ID = "someID" };
您自己设置了 public 字段,并且使用了空构造函数。
无论哪种方式,您都应该避免使用 public 字段,因为它不安全。你不应该让任何外部的人像这样直接修改它们。而是仅使用私有字段并使用 public 属性来管理来自外部的访问!
Customer c1 = new Customer {};
这是一个空的对象初始值设定项。根据 spec,对象初始值设定项将调用默认构造函数,除非您指定要使用的构造函数。由于没有进行初始化,所以会像使用默认构造函数一样编译。
要回答这个问题,如果“{}
在 C# 中创建新对象时表现得像 ()
”,我们必须更详细地说明。对于您关心的所有内容,生成的对象将包含相同的数据,但生成的 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(仅主要方法,不进行优化调试)
.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,我们看到创建了一个新对象并将其分配给不可见的临时变量 <>g__initLocal0
,只有在完成之后,结果值分配给 c3
。这就是 C# 编译器实现对象初始值设定项的方式。当您查看 c4
如何从 IL_0042 初始化为 IL_006a 时,在实例化对象后设置 public 属性 "manually" 的区别很明显 - 没有临时变量。
IL_0070 直到最后都等同于前面的示例,除了它们使用非默认构造函数和对象初始值设定项。如您所料,它只是按照与以前相同的方式进行编译,但使用指定的构造函数。
长话短说:从 C# 开发人员的角度来看,结果基本相同。对象初始值设定项是简单的语法糖和编译器技巧,有助于使代码更易于阅读。但是 FWIW,生成的 IL 与属性的手动初始化不同。尽管如此,编译器发出的不可见临时变量的成本在几乎所有 C# 程序中都应该是微不足道的。