属性初始值设定项和C#中简单值的表达式之间有区别吗?

属性初始值设定项和C#中简单值的表达式之间有区别吗?,c#,C#,这两个属性在C#中有区别吗?第一个属性是否只实例化一次,而第二个属性在每次访问该属性时实际实例化一个新的字符串对象 public string Property1 { get; } = "Property1"; public string Property2 => "Property2"; 是的,有区别。第一个声明为只读属性: public string Property1 { get; } = "Property1"; .property instance string ReadOn

这两个属性在C#中有区别吗?第一个属性是否只实例化一次,而第二个属性在每次访问该属性时实际实例化一个新的字符串对象

public string Property1 { get; } = "Property1";
public string Property2 => "Property2";

是的,有区别。第一个声明为只读属性:

public string Property1 { get; } = "Property1";
.property instance string ReadOnlyLambda()
{
  .get instance string ScratchCsConsole.Program::get_ReadOnlyLambda()
} // end of property Program::ReadOnlyLambda
.method public hidebysig specialname instance string 
        get_ReadOnlyLambda() cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldstr      "String B"
  IL_0005:  ret
} // end of method Program::get_ReadOnlyLambda
这是简写/句法上的糖

private readonly string $property1 = "Property1";
public string Property1 { get { return $property1; } }
其中,
$property1
是一些编译器生成的字段

第二个使用速记getter声明属性:

public string Property2 => "Property2";
这是以下的简写:

public string Property2 { get { return "Property2"; } }
它根本没有一个支持字段

第二种方法实际上并没有“实例化一个新的字符串对象”,因为字符串是只读的,并且字符串常量在编译时被保存;它每次只返回相同的字符串实例

在这种特殊情况下,您几乎不会注意到差异,因为只有通过反射明确查看属性的对象才会注意到支持字段的存在或不存在。通过计算表达式的非平凡getter,事情变得更加有趣:

private static string a = "a";
private static string b = "b";
public string Property1 { get; } = a + b;
public string Property2 => a + b;

Console.WriteLine(Property1 == Property2);   // true, since "ab" == "ab"
a = "no more a";
Console.WriteLine(Property1 == Property2);   // false, since "ab" != "no more ab"

(当然,这是一个人为的示例,但它显示了只读属性和计算属性之间的区别。)

要在另一个答案中添加一些信息,请在编译具有这两个属性的类时:

public string ReadOnlyInitialized { get; } = "String A";
public string ReadOnlyLambda => "String B";
以下IL是为
ReadOnlyInitialized
属性生成的

.field private initonly string '<ReadOnlyInitialized>k__BackingField'
// snipped debugger attributes    

.property instance string ReadOnlyInitialized()
{
  .get instance string ScratchCsConsole.Program::get_ReadOnlyInitialized()
} // end of property Program::ReadOnlyInitialized

.method public hidebysig specialname instance string 
        get_ReadOnlyInitialized() cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string ScratchCsConsole.Program::'<ReadOnlyInitialized>k__BackingField'
  IL_0006:  ret
} // end of method Program::get_ReadOnlyInitialized

正如您在这个版本中所看到的,唯一的区别是字符串现在作为立即数加载到堆栈中,而不是从后台字段加载。

这是正确的语法吗?@easymoney202是,它对C#6有效。(VS2015附带的版本)在你回答之后,我写了一个在结构和内容上与你的答案几乎相同的答案。我已经把它删除了。您可能想展示一个“使用计算表达式的非平凡getter使事情变得更有趣”的示例,这有助于澄清答案。感谢Jeroen和Jacob。为了支持此示例,删除了我的示例。我不知道支持字段的差异。回答得很好。@JacobKrall:怎么样?太好了!任何使用
new
的东西都是一个很好的例子。