自动属性初始值设定项

介绍

关闭 } 后,可以使用 = 运算符初始化属性。下面的 Coordinate 类显示了初始化属性的可用选项:

Version >= 6
public class Coordinate
{ 
    public int X { get; set; } = 34; // get or set auto-property with initializer

    public int Y { get; } = 89;      // read-only auto-property with initializer              
}

具有不同可见性的存取器

你可以初始化对其访问者具有不同可见性的自动属性。以下是受保护的 setter 的示例:

    public string Name { get; protected set; } = "Cheeze";

存取器也可以是 internalinternal protectedprivate

只读属性

除了具有可见性的灵活性之外,你还可以初始化只读自动属性。这是一个例子:

    public List<string> Ingredients { get; } = 
        new List<string> { "dough", "sauce", "cheese" };

此示例还说明了如何使用复杂类型初始化属性。此外,自动属性不能是只写的,因此也会阻止只写初始化。

旧式(前 C#6.0)

在 C#6 之前,这需要更详细的代码。我们使用一个名为 backing 属性的额外变量为属性提供默认值或初始化公共属性,如下所示,

Version < 6
public class Coordinate
{
    private int _x = 34;
    public int X { get { return _x; } set { _x = value; } }

    private readonly int _y = 89;
    public int Y { get { return _y; } }
    
    private readonly int _z;
    public int Z { get { return _z; } }

    public Coordinate()
    {
        _z = 42;
    }
}

注意: 在 C#6.0 之前,你仍然可以在构造函数中初始化读取和写入自动实现的属性 (带有 getter 和 setter 的属性),但是你无法使用其声明内联初始化属性

查看演示

用法

初始化器必须评估为静态表达式,就像字段初始化器一样。如果需要引用非静态成员,可以像之前一样初始化构造函数中的属性,或者使用表达式 - 身体属性。非静态表达式,如下面的那个(已注释掉),将生成编译器错误:

// public decimal X { get; set; } = InitMe();  // generates compiler error

decimal InitMe() { return 4m; }

但静态方法用于初始化自动属性:

public class Rectangle
{
    public double Length { get; set; } = 1;
    public double Width { get; set; } = 1;
    public double Area { get; set; } = CalculateArea(1, 1);

    public static double CalculateArea(double length, double width)
    {
        return length * width;
    }
}

此方法还可以应用于具有不同访问级别的属性:

public short Type { get; private set; } = 15;

auto-property 初始化程序允许直接在其声明中分配属性。对于只读属性,它会处理确保属性不可变所需的所有要求。例如,考虑以下示例中的 FingerPrint 类:

public class FingerPrint
{
  public DateTime TimeStamp { get; } = DateTime.UtcNow;

  public string User { get; } =
    System.Security.Principal.WindowsPrincipal.Current.Identity.Name;

  public string Process { get; } =
    System.Diagnostics.Process.GetCurrentProcess().ProcessName;
}

查看演示

警示说明

注意不要将自动属性或字段初始化程序与使用 => 而不是 = 的类似外观的表达体方法以及不包含 { get; } 的字段混淆。

例如,以下每个声明都是不同的。

public class UserGroupDto
{
    // Read-only auto-property with initializer:       
    public ICollection<UserDto> Users1 { get; } = new HashSet<UserDto>();
    
    // Read-write field with initializer:
    public ICollection<UserDto> Users2 = new HashSet<UserDto>();

    // Read-only auto-property with expression body:
    public ICollection<UserDto> Users3 => new HashSet<UserDto>();
}

在属性声明中缺少 { get; } 会导致公共字段。只读自动属性 Users1 和读写字段 Users2 都只初始化一次,但是公共字段允许从类外部更改集合实例,这通常是不合需要的。使用表达式主体将只读自动属性更改为具有初始化程序的只读属性不仅需要从 => 中删除 >,还需要添加 { get; }

Users3 中的不同符号(=> 而不是 =)导致每次访问属性都返回 HashSet<UserDto> 的新实例,而有效的 C#(从编译器的角度来看)在用于集合时不太可能是期望的行为会员。

以上代码相当于:

public class UserGroupDto
{
    // This is a property returning the same instance
    // which was created when the UserGroupDto was instantiated.
    private ICollection<UserDto> _users1 = new HashSet<UserDto>();
    public ICollection<UserDto> Users1 { get { return _users1; } }

    // This is a field returning the same instance
    // which was created when the UserGroupDto was instantiated.
    public virtual ICollection<UserDto> Users2 = new HashSet<UserDto>();

    // This is a property which returns a new HashSet<UserDto> as
    // an ICollection<UserDto> on each call to it.
    public ICollection<UserDto> Users3 { get { return new HashSet<UserDto>(); } }
}