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相关推荐

  1. Effective JavaScript Item 37 认识this的隐式指向

    本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔.所以在实现CSV Reader时,须要支持不同的分隔符.那么,非常自然的一种实现就是将分隔符作为 ...

  2. Effective JavaScript Item 23 永远不要修改arguments对象

    本系列作为Effective JavaScript的读书笔记. arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等.因此调用诸如:argument ...

  3. More Effective C++ Item 附2:一个auto_ptr的实现实例

    More Effective C++的前言.导读和附1(侯捷译),以及在 "C++ 中计算物件个数"和"为智能指标实作 operator->*"(陈崴译, ...

  4. Effective Java - Item 1: Consider static factory methods instead of constructors

    考虑使用静态工厂方法来替代构造方法, 这样的做的好处有四点. 1. 更好的表意 有的构造方法实际上有特殊的含义, 使用静态工厂方法能更好的表达出他的意思. 例如 BigInteger(int, int ...

  5. Effective C++ Item 30 inline里里外外

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie Item 44 46 1.将大多数 inlining 限制在小型.被频繁调用的函数身上.这可 ...

  6. 读书笔记 effective c++ Item 50 了解何时替换new和delete 是有意义的

    1. 自定义new和delete的三个常见原因 我们先回顾一下基本原理.为什么人们一开始就想去替换编译器提供的operator new和operator delete版本?有三个最常见的原因: 为了检 ...

  7. 读书笔记 effective c++ Item 49 理解new-handler的行为

    1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常.很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做.你仍然会看到这种旧行为,但是我会把关于它的讨 ...

  8. 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

    1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些"讲道理"的人员,这些客户尝试把 ...

  9. 读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式

    1. 一个错误释放内存的例子 下面的场景会有什么错? 1 std::string *stringArray = new std::string[100]; 2 3 ... 4 5 delete str ...

最新文章

  1. python二元函数求导_用Excel和python实现二元函数梯度下降的人工智能,之用,excel,一元...
  2. 2022年如何学习自动化测试?这篇文章告诉你
  3. jquery动态添加元素无法触发绑定事件的解决方案。
  4. 皮一皮:网络延迟的好处...
  5. 中国最难考的大学共有31所,分6大梯度,考上毕业很抢手
  6. Python使用openpyxl读写excel文件
  7. PL/SQL Developer如何修改表数据
  8. 你只知道JVM栈,知不知道栈帧、局部变量表、slot、操作数栈?
  9. Springboot @Value获取配置文件中的值失效
  10. 前端又省事了,Chrome直接支持lazyload延迟加载
  11. RocketMq发送延迟消息
  12. 27个澳洲年轻人,重演了少年马云的一段奇遇
  13. SparkMLlib分类算法之决策树学习
  14. 我不是九爷 带你了解 ansible
  15. python中threading模块_举例详解Python中threading模块的几个常用方法
  16. python实用源码_最实用python教学视频,麻瓜编程含源码+课件+课后习题
  17. sin30度用c语言转换弧度制,【数学】弧度和角度的转换
  18. Proxy和Reflect
  19. FLOWABLE 流程中的自动跳过
  20. C. Product 1 Modulo N

热门文章

  1. 互联网秒杀设计--转载
  2. 【cogs 309】香甜的黄油
  3. C#编程(四十五)----------格式字符串
  4. 三星拆分将近 董事会决定先创建控股公司
  5. mysql 主从报错
  6. .net c# 正则表达式 平衡组/递归匹配
  7. 使用PLP特征训练crnn语音分类
  8. 自编码网络这样应用到任务上可以吗
  9. jQuery 之 serialize() serializeArray()
  10. Excel直接转图片