自動屬性初始值設定項

介紹

關閉 } 後,可以使用 = 運算子初始化屬性。下面的 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>(); } }
}