原则1:始终能的使用属性(property),而不是可直接访问的Data Member

Always useproperties instead of accessible data members.

在C#里,Property已经晋升为一类公民。如果你的类里还有Public的变量,Stop! 如果你还在手写get and set 方法,Stop! Property在不破坏你类的封装的情况下,仍可以把类的datamember变成public interface的一部分。访问Property的方式和访问data member的方式一样,但Property是用methods实现的。

有些类的成员只能用data最好的表示,比如:你一个客户的名字,一个点的坐标,等等。而Property就是用来欺骗使用你类的客户,让它们错误的认为它们在访问你类的public变量。你还可以通过Property的实现方法来控制Property的访问。

.Net Framework假定你使用Property来让外界访问你类里想让外界访问到的data member (也就是public data member) 。实际上也是这样的,因为.Net的data binding只支持Property,而不支持public data member的访问。Data binding的目的就是把一个object的Property绑定到一个用户界面的control上,web control或者windows form control. Data binding是通过reflection来实现的,如下例:

textBoxCity.DataBindings.Add("Text",address, "City");

这段code就是把textBoxCity的Text Property绑定到address这个object的City Property上。如果你把address的City Property改成public data member,这段code是不会运行的。因为.Net Framework Class Library的设计者不支持你的这种行为,他们认为public data member是非常不好的行为和习惯,所以他们不会支持,他们想让你遵从正确的Object Oriented设计方法。Data binding也不会去找get and set methods,所以一定要用Property,而不是传统的get and set methods.

你也许要说,databinding只适用于那些含有要显示在用户界面的元素的类。但实际情况并不是这样,对于你所有的类,都要使用Property而不是public data member。因为当有新的需求时,通过修改Property的实现方法来适应这个新的需求,要比在你的程序里修改所有的public data member去适应这个需求容易太多了。比如说你以前定义了一个类customer,现在你发现由于当初的粗心没有强制customer姓名不能为空,如果你使用了Property,你可以非常轻松的添加一个检查机制,如下面这段code:

public class Customer

{

    private string _name;

    public string Name

    {

        get

        {

            return _name;

        }

        set

        {

            if ((value == null) ||(value.Length == 0))

            {

                throw new ArgumentException("Namecan    not be blank","Name");

            }

            _name = value;

        }

    }

    //...

}

如果你使用了publicdata member,你就要找遍你的程序,在每个地方都修改,那样就很愚蠢了。而且浪费了无数青春好时光。

因为Property是用methods实现的,所以添加multi-threaded的支持是非常方便的。比如想要添加同步访问的支持:

public string Name

{

    get

    {

        lock(this)

        {

            return _name;

        }

    }

    set

    {

        lock(this)

        {

            _name = value;

        }

    }

}

因为Property是用methods实现的,所以它拥有methods所拥有的一切。Property可以被定义为virtual:

public class Customer

{

    private string _name;

    public virtual string Name

    {

        get

        {

            return _name;

        }

        set

        {

            _name = value;

        }

    }

    //...

}

显而易见,你也可以把Property扩展为abstract,甚至成为interface的一部分。

public interfaceINameValuePair

{

    object Name

    {

        get;

    }

    object Value

    {

        get;

        set;

    }

}

你当然也可以扩展出const和nonconst版本的interface。

public interfaceIConstNameValuePair

{

    object Name

    {

        get;

    }

    object Value

    {

        get;

    }

}

public interfaceINameValuePair

{

    object Value

    {

        get;

        set;

    }

}

//usage:

public class Stuff :IConstNameValuePair, INameValuePair

{

    private string _name;

    private object _value;

    #region IConstNameValuePair Members

    public object Name

    {

        get

        {

            return _name;

        }

    }

    object IConstNameValuePair.Value

    {

        get

        {

            return _value;

        }

    }

    #endregion

    #region INameValuePair Members

    public object Value

    {

        get

        {

            return _value;

        }

        set

        {

            _value = value;

        }

    }

    #endregion

}

如前所述,Property是访问内部数据的method的扩展,它拥有member function的一切特性。

因为实现Property访问的方法get and set是独立的两个method,在C# 2.0中,你可以给它们定义不同的访问级别,来更好的控制类成员的可见性,如下例:

public class Customer

{

    private string _name;

    public virtual string Name

    {

        get

        {

            return _name;

        }

        protected set

        {

            _name = value;

        }

    }

    //...

}

Property的语法已经超越了单纯的data field。如果你的类包含indexed item,你可以使用indexer(参数化的Property),你可以创建一个可返回一个序列元素的Property,如下例:

public int this[int index]

{

    get

    {

          return _theValues[index];

    }

    set

    {

          _theValues[index] = value;

    }

}

//usage:

int val = MyObject[i];

indexer和单元素Property有着相同的特性。一维的indexer可以用于databinding,二维和多维的indexer可以用来实现其他的数据结构,比如map和dictionary:

public Address this[stringname]

{

    get

    {

        return _theValues[name];

    }

    set

    {

        theValues[ name ] = value;

    }

}

多维的indexer的每个axis上的数据类型可以相同,也可以不同:

public int this[int x, inty]

{

    get

    {

        return ComputeValue(x, y);

    }

}

public int this[int x,string name]

{

    get

    {

        return ComputeValue(x, name);

    }

}

所有的indexer都必须也只能用this来定义,所以参数表相同的indexer,每个类最多只能有一个。

因为使用Property和datamember对于数据访问的code没有什么区别,比如:

public class Customer

{

    public string Name;

    //...

}

在这个类中使用了publicdata member,数据访问的code如下:

string name =CustomerOne.Name;

CustomerOne.Name ="customer name";

你也许会想,如果在以后的修改中,用Property来代替publicdata member是可行的,因为数据访问的code相同,但实际上这是行不通的。确实,访问Property和访问datamember的code是相同的,但Property不是data,访问Property所产生的IL code和数据访问的IL code是不一样的。所以访问Property和访问datamember只具有code兼容性,而不具有binary的兼容性。如果有兴趣,你可以使用Reflector(http://www.aisto.com/roeder/dotnet/)来分析使用Property和publicdata member的类。

你会发现在使用Property的类中,存在.propertydirective,这个directive定义了Property的类型以及get andset实现方法。Getand set都被标注为hidebysig,specialname。也就是说它们不能被C#源代码直接调用,它们也不是正是的类型定义。你只能通过Property来访问它们。

C#的编译器会根据类的情况(是用Property还是datamember)来自动产生不同的ILcode。如上所述,访问Property和访问datamember只具有code兼容性,而不具有binary的兼容性。所以,如果你改变最初的设计,用Property来代替publicdata member的话,你必须重新编译整个程序。这使得升级已经部署的程序或assembly是非常的麻烦。

那么两种实现谁的效率更好呢?Property确实不会比publicdata member快,但也不一定会慢。因为JIT对Property的存取方法set andget进行inline的优化。这时,Property和publicdata member的效率是一样的。即使Property的存取方法没有被inline优化,它和publicdata member的效率差别也只是一个可以忽略的functioncall。只有在很少的情况下,这种差别才可以被测量出来。

总而言之,当你想让你类内部的数据被外界访问到时(不管是public还是protected),一定要用Property。对于序列和字典,使用indexer。你类的datamember永远应该是private,绝无例外。使用Property,你可以得到如下好处:

1.Data binding支持

2.对于需求变化有更强的适应性,更方便的修改实现方法

记住,现在多花1分钟使用Property,会在你修改程序以适应设计变化时,为你节约n小时。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值