Effective C# Item 8: Ensure That 0 Is a Valid State for Value Types

Effective C# Item 8 : Ensure That 0 Is a Valid State for Value Types

      .Net中默认的初始化是将对象的所有信息都置0。对于值类型来说,我们不能避免在创建新的值类型实例的时候将其值置为0。这是类型的默认值。

      唯一一个特殊情况是枚举(enum)。我们不应该创建一个不包含0作为其值的枚举。所有的枚举都继承自System.ValueType。枚举值是从0开始的,但是我们可以修改它:(P.S. 虽然现在Pluto已经不是太阳系的一员了,bless一下)

None.gif public   enum  Planet
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      Mercury 
= 1,
InBlock.gif      Venus 
= 2,
InBlock.gif      Earth 
= 3,
InBlock.gif      Mars 
= 4,
InBlock.gif      Jupiter 
= 5,
InBlock.gif      Saturn 
= 6,
InBlock.gif      Neptune 
= 7,
InBlock.gif      Uranus 
= 8,
InBlock.gif      Pluto 
= 9
ExpandedBlockEnd.gif}

None.gif
None.gifPlanet sphere 
=   new  Planet();

      现在sphere的值就是0,这不是一个合法的值。虽然不会报错,但是0并不代表任何含义。当我们创建自己的引用时,应当确保0是其中的一个枚举值。如果你将枚举声明为二进制类型的,需要定义0来代表None。

      如果这样发展下去的话,我们只能强迫用户在初始化的时候就为其明确赋值。

None.gif Planet sphere  =  Planet.Mars;

      这将使得我们很难在其他的类型中使用这种类型,因为我们并不知道要将其初始化为何值。

      使用这样的类型创建对象将会创建一个不合法的Planet。

None.gif ObservationData d  =   new  ObservationData();

      我们必须确保0是一个有效的值。如果可以的话应当将其对应为为最佳默认值。对于Planet枚举来说,没有什么最佳可言。当用户没有使用的时候,它应当不包含任何含义。我们可以使用0来改造我们的枚举使其更加合理:

None.gif public   enum  Planet
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      None 
= 0,
InBlock.gif      Mercury 
= 1,
InBlock.gif      Venus 
= 2,
InBlock.gif      Earth 
= 3,
InBlock.gif      Mars 
= 4,
InBlock.gif      Jupiter 
= 5,
InBlock.gif      Saturn 
= 6,
InBlock.gif      Neptune 
= 7,
InBlock.gif      Uranus 
= 8,
InBlock.gif      Pluto 
= 9
ExpandedBlockEnd.gif}

None.gif
None.gifPlanet sphere 
=   new  Planet();

      sphere现在对应的枚举值是None,代表还没有用户数据。在这里添加默认值对ObservationData造成的影响就是新初始化的对象的枚举值是0,代表的含义是None。之后可以在ObservationData的构造函数中对其进行修改。要注意的是默认的构造函数仍然有效。用户可以构造一个默认值的对象,我们不能阻止他们这样做。

      在我们结束enum的讨论之前,我们还有必要说一下在使用flag的时候枚举的一些特殊情况。使用Flags属性时我们应当将代表None含义的枚举值设为0:(P,S.关于Flags枚举的情况请看我写的另一篇介绍:FlagsAttribute属性在enum中的应用)

None.gif [Flags]
None.gif
public   enum  Styles
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      None 
= 0,
InBlock.gif      Flat 
= 1,
InBlock.gif      Sunken 
= 2,
InBlock.gif      Raised 
= 4
ExpandedBlockEnd.gif}

      许多开发者通过二进制操作符&来使用flags枚举。不合理的0值会造成严重的问题,如果flag为0值则下例中的条件会永远不能成立:

None.gif if ((flag  &  Styles.Flat)  !=   0 )
None.gif      DoSth();    
// 如果flag为0则永远不会执行

      因此如果使用Flags,我们应当确定0代表的含义是None。

      另外的常见初始化问题发生在值类型中包含的引用类型上。String就是一个常见的例子(P.S. .Net中string是引用类型,虽然某些操作符的重载会给用户带来值类型的错觉)。

None.gif public   struct  LogMessage
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
private int _ErrLevel;
InBlock.gif      
private string _msg;
ExpandedBlockEnd.gif}

None.gif
None.gifLogMessage MyMessage 
=   new  LogMessage();

      MyMessage包含了一个为null的引用类型。这样并不会引起任何问题。但是当你使用属性来获取它的值的时候就可能会出现问题。因此我们要为其添加如果为null则返回string.Empty的逻辑。在我们的结构中我们应当这样检验属性是否为null。

None.gif public   struct  LogMessage
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      
private int _ErrLevel;
InBlock.gif      
private string _msg;
InBlock.gif
InBlock.gif      
public string Message
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  
return(_msg != null)? _msg:string.Empty;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                  _msg 
= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif      }

ExpandedBlockEnd.gif}

      系统初始化时所有的类型的值都为0,这是不能避免的。可能的话应当将0设为最初的默认值。特殊情况下,例如flags枚举,应当让其代表None。

      译自   Effective C#:50 Specific Ways to Improve Your C#                      Bill Wagner著

      回到目录

转载于:https://www.cnblogs.com/aiyagaze/archive/2006/09/16/505614.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值