在 C# 6 之前,属性的初始化不使用支持字段来初始化默认值。 在 C#6 中,它使用支持字段来初始化新的 Auto initialization properties .
我很好奇为什么在 C#6 之前 IL 使用属性定义来初始化。这有什么具体原因吗?还是在 C#6 之前没有正确实现?
C# 6.0 之前
public class PropertyInitialization
{
public string First { get; set; }
public string Last { get; set; }
public PropertyInitialization()
{
this.First = "Adam";
this.Last = "Smith";
}
}
编译器生成的代码(IL 表示)
public class PropertyInitialisation
{
[CompilerGenerated]
private string \u003CFirst\u003Ek__BackingField;
[CompilerGenerated]
private string \u003CLast\u003Ek__BackingField;
public string First
{
get
{
return this.\u003CFirst\u003Ek__BackingField;
}
set
{
this.\u003CFirst\u003Ek__BackingField = value;
}
}
public string Last
{
get
{
return this.\u003CLast\u003Ek__BackingField;
}
set
{
this.\u003CLast\u003Ek__BackingField = value;
}
}
public PropertyInitialisation()
{
base.\u002Ector();
this.First = "Adam";
this.Last = "Smith";
}
}
C#6
public class AutoPropertyInitialization
{
public string First { get; set; } = "Adam";
public string Last { get; set; } = "Smith";
}
编译器生成的代码(IL 表示)
public class AutoPropertyInitialization
{
[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string \u003CFirst\u003Ek__BackingField;
[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string \u003CLast\u003Ek__BackingField;
public string First
{
get
{
return this.\u003CFirst\u003Ek__BackingField;
}
set
{
this.\u003CFirst\u003Ek__BackingField = value;
}
}
public string Last
{
get
{
return this.\u003CLast\u003Ek__BackingField;
}
set
{
this.\u003CLast\u003Ek__BackingField = value;
}
}
public AutoPropertyInitialization()
{
this.\u003CFirst\u003Ek__BackingField = "Adam";
this.\u003CLast\u003Ek__BackingField = "Smith";
base.\u002Ector();
}
}
最佳答案
I'm curious why prior to C#6 IL uses the property definition to initialize. Is there a specific reason for this?
因为通过自动属性初始化设置值和在构造函数中设置值是两件不同的事情。他们有不同的行为。
回想一下,属性是环绕字段的访问器方法。所以这一行:
this.First = "Adam";
相当于:
this.set_First("Adam");
您甚至可以在 Visual Studio 中看到这一点!尝试在您的类中编写一个带有签名 public string set_First(string value)
的方法,然后观察编译器是否提示您踩到了它的脚趾。
就像方法一样,这些可以在子类中被覆盖。查看这段代码:
public class PropertyInitialization
{
public virtual string First { get; set; }
public PropertyInitialization()
{
this.First = "Adam";
}
}
public class ZopertyInitalization : PropertyInitialization
{
public override string First
{
get { return base.First; }
set
{
Console.WriteLine($"Child property hit with the value: '{0}'");
base.First = value;
}
}
}
在此示例中,this.First = "Adam"
行将调用子类中的 setter。因为你正在调用一个方法,还记得吗?如果编译器将此方法调用解释为对支持字段的直接调用,则它最终不会调用子 setter。 编译代码的行为会改变程序的行为。不好!
自动属性不同。让我们使用自动属性初始值设定项来更改第一个示例:
public class PropertyInitialization
{
public virtual string First { get; set; } = "Adam";
}
public class ZopertyInitalization : PropertyInitialization
{
public override string First
{
get { return base.First; }
set
{
Console.WriteLine($"Child property hit with the value: '{0}'");
base.First = value;
}
}
}
有了这段代码,子类中的setter方法就不会被调用了。这是故意的。自动属性初始化器旨在直接设置支持字段。它们看起来和行为都像字段初始化器,这就是为什么我们甚至可以在没有 setter 的属性上使用它们,如下所示:
public string First { get; } = "Adam";
这里没有setter方法!我们必须直接访问支持字段才能执行此操作。自动属性允许程序员创建不可变的值,同时仍然能够从良好的语法中受益。
关于C# 6 自动初始化属性和支持字段的使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39840684/