使用屬性而不是可訪問的資料成員

weixin_33912246發表於2017-01-05
  • 在日後產生新的需求或行為時,屬性更易於修改。例如,你很快會覺得客戶物件不應該有空白的名稱。若你之前使用了公有屬性來封裝Name,那麼只需要修改一處即可:
public class Customer
{
    private string m_Name;
    public string Name
    {
        get { return m_Name; }

        set
        {
            if (string.IsNullOrEmpty(value))
                throw new System.ArgumentException("Name can not be blank.", "m_Name");

            m_Name = value;
        }
    }
}
  • 因為屬性是使用方法來實現的,所以新增多執行緒支援也非常簡單:
public class Customer
{
    private object m_SyncHandle = new object();

    private string m_Name;
    public string Name
    {
        get
        {
            lock(m_SyncHandle)
                return m_Name;
        }

        set
        {
            if (string.IsNullOrEmpty(value))
                throw new System.ArgumentException("Name can not be blank.", "m_Name");

            lock(m_SyncHandle)
                m_Name = value;
        }
    }
}
  • 屬性可以擁有方法的所有語言特性,例如:屬性可以是虛的:
public class Customer
{
    public virtual string Name
    {
        get;
        set;
    }
}

也可以是抽象的:

public interface INameValuePair<T>
{
    string Name
    {
        get;
    }

    T Value
    {
        get;
        set;
    }
}

需要注意的是,雖然抽象的屬性其語法和隱式屬性完全相同,但是編譯器不會自動地生成任何實現。介面只是定義了一個契約,強制所有實現了該介面的型別都必須滿足。

  • 有時候你可能會想能不能先用資料成員來實現,然後在稍後需要其他各種功能的時候再改成屬性呢?看上去是個不錯的策略,但實際上卻行不通。考慮以下這個類的定義:
public class Customer
{
    public string Name;
}
 
// Client:
Customer enigmaJJ = new Customer();
enigmaJJ.Name = "EnigmaJJ";

看似以上的程式碼在日後若是將Name改成屬性,那麼程式碼本身也可以無需修改而保持正常。但這並不是完全正確的。屬性並不是資料,屬性的訪問和資料的訪問將會生成不同的MSIL(Microsoft Intermediate Language)指令。也就是說,雖然屬性和資料成員在原始碼層次上是相容的,不過在二進位制層面上卻大相徑庭。這也意味著,若將資料成員改成了與之等同的屬性,那麼就必須重新編譯所有用到該資料成員的程式碼。C#語言本身的一個目標就是支援釋出某個單一程式集時,不需要更新整個應用程式。而這個將資料成員改為屬性的簡單操作卻破壞了二進位制相容性,也會讓更新單一程式集變得非常困難。

相關文章