使用属性而不是可访问的数据成员

  • 在日后产生新的需求或行为时,属性更易于修改。例如,你很快会觉得客户对象不应该有空白的名称。若你之前使用了公有属性来封装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
{
    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#语言本身的一个目标就是支持发布某个单一程序集时,不需要更新整个应用程序。而这个将数据成员改为属性的简单操作却破坏了二进制兼容性,也会让更新单一程序集变得非常困难。

你可能感兴趣的:(使用属性而不是可访问的数据成员)