Effective C#: Item 1 Always use properties instead of accessible data members
Effective C#: Item 1 Always use properties instead of accessible data members
Item 1: 当设计类时,永远用Property, 而不是可直接访问的Data Member
在C#里,Property已经晋升为一类公民。如果你的类里还有Public的变量,Stop! 如果你还在手写get and set 方法,Stop! Property在不破坏你类的封装的情况下,仍可以把类的data member变成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.
你也许要说,data binding只适用于那些含有要显示在用户界面的元素的类。但实际情况并不是这样,对于你所有的类,都要使用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( "Name can not be blank", "Name");
}
_name = value;
}
}
//...
}
如果你使用了public data 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 interface INameValuePair
{
object Name
{
get;
}
object Value
{
get;
set;
}
}
你当然也可以扩展出const和nonconst版本的interface。
public interface IConstNameValuePair
{
object Name
{
get;
}
object Value
{
get;
}
}
public interface INameValuePair
{
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可以用于data binding,二维和多维的indexer可以用来实现其他的数据结构,比如map和dictionary:
public Address this [ string name ]
{
get
{
return _theValues[ name ];
}
set
{
_theValues[ name ] = value;
}
}
多维的indexer的每个axis上的数据类型可以相同,也可以不同:
public int this [ int x, int y ]
{
get
{
return ComputeValue( x, y );
}
}
public int this [ int x, string name ]
{
get
{
return ComputeValue( x, name );
}
}
所有的indexer都必须也只能用this来定义,所以参数表相同的indexer,每个类最多只能有一个。
因为使用Property和data member对于数据访问的code没有什么区别,比如:
public class Customer
{
public string Name;
//...
}
在这个类中使用了public data member,数据访问的code如下:
string name = CustomerOne.Name;
CustomerOne.Name = "customer name";
你也许会想,如果在以后的修改中,用Property来代替public data member是可行的,因为数据访问的code相同,但实际上这是行不通的。确实,访问Property和访问data member的code是相同的,但Property不是data,访问Property所产生的IL code和数据访问的IL code是不一样的。所以访问Property和访问data member只具有code兼容性,而不具有binary的兼容性。如果有兴趣,你可以使用Reflector (http://www.aisto.com/roeder/dotnet/ )来分析使用Property和public data member的类。
你会发现在使用Property的类中,存在.property directive,这个directive定义了Property的类型以及get and set实现方法。Get and set都被标注为hidebysig, specialname。也就是说它们不能被C#源代码直接调用,它们也不是正是的类型定义。你只能通过Property来访问它们。
C#的编译器会根据类的情况(是用Property还是data member)来自动产生不同的IL code。如上所述,访问Property和访问data member只具有code兼容性,而不具有binary的兼容性。所以,如果你改变最初的设计,用Property来代替public data member的话,你必须重新编译整个程序。这使得升级已经部署的程序或assembly是非常的麻烦。
那么两种实现谁的效率更好呢?Property确实不会比public data member快,但也不一定会慢。因为JIT对Property的存取方法set and get进行inline的优化。这时,Property和public data member的效率是一样的。即使Property的存取方法没有被inline优化,它和public data member的效率差别也只是一个可以忽略的function call。只有在很少的情况下,这种差别才可以被测量出来。
总而言之,当你想让你类内部的数据被外界访问到时(不管是public还是protected),一定要用Property。对于序列和字典,使用indexer。你类的data member永远应该是private,绝无例外。使用Property,你可以得到如下好处:
1.Data binding支持
2.对于需求变化有更强的适应性,更方便的修改实现方法
记住,现在多花1分钟使用Property,会在你修改程序以适应设计变化时,为你节约n小时。
本系列文章只是作者读书笔记,版权完全属于原作者 (Bill Wagner),任何人及组织不得以任何理由以商业用途使用本文,任何对本文的引用和转载必须通知作者:zphillm@hotmail.com
转载于:https://www.cnblogs.com/ZphillM/archive/2005/02/22/107245.html
Effective C#: Item 1 Always use properties instead of accessible data members相关推荐
- Effective JavaScript Item 37 认识this的隐式指向
本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔.所以在实现CSV Reader时,须要支持不同的分隔符.那么,非常自然的一种实现就是将分隔符作为 ...
- Effective JavaScript Item 23 永远不要修改arguments对象
本系列作为Effective JavaScript的读书笔记. arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等.因此调用诸如:argument ...
- More Effective C++ Item 附2:一个auto_ptr的实现实例
More Effective C++的前言.导读和附1(侯捷译),以及在 "C++ 中计算物件个数"和"为智能指标实作 operator->*"(陈崴译, ...
- Effective Java - Item 1: Consider static factory methods instead of constructors
考虑使用静态工厂方法来替代构造方法, 这样的做的好处有四点. 1. 更好的表意 有的构造方法实际上有特殊的含义, 使用静态工厂方法能更好的表达出他的意思. 例如 BigInteger(int, int ...
- Effective C++ Item 30 inline里里外外
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie Item 44 46 1.将大多数 inlining 限制在小型.被频繁调用的函数身上.这可 ...
- 读书笔记 effective c++ Item 50 了解何时替换new和delete 是有意义的
1. 自定义new和delete的三个常见原因 我们先回顾一下基本原理.为什么人们一开始就想去替换编译器提供的operator new和operator delete版本?有三个最常见的原因: 为了检 ...
- 读书笔记 effective c++ Item 49 理解new-handler的行为
1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常.很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做.你仍然会看到这种旧行为,但是我会把关于它的讨 ...
- 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用
1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些"讲道理"的人员,这些客户尝试把 ...
- 读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式
1. 一个错误释放内存的例子 下面的场景会有什么错? 1 std::string *stringArray = new std::string[100]; 2 3 ... 4 5 delete str ...
最新文章
- python二元函数求导_用Excel和python实现二元函数梯度下降的人工智能,之用,excel,一元...
- 2022年如何学习自动化测试?这篇文章告诉你
- jquery动态添加元素无法触发绑定事件的解决方案。
- 皮一皮:网络延迟的好处...
- 中国最难考的大学共有31所,分6大梯度,考上毕业很抢手
- Python使用openpyxl读写excel文件
- PL/SQL Developer如何修改表数据
- 你只知道JVM栈,知不知道栈帧、局部变量表、slot、操作数栈?
- Springboot @Value获取配置文件中的值失效
- 前端又省事了,Chrome直接支持lazyload延迟加载
- RocketMq发送延迟消息
- 27个澳洲年轻人,重演了少年马云的一段奇遇
- SparkMLlib分类算法之决策树学习
- 我不是九爷 带你了解 ansible
- python中threading模块_举例详解Python中threading模块的几个常用方法
- python实用源码_最实用python教学视频,麻瓜编程含源码+课件+课后习题
- sin30度用c语言转换弧度制,【数学】弧度和角度的转换
- Proxy和Reflect
- FLOWABLE 流程中的自动跳过
- C. Product 1 Modulo N