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一下)

public enum Planet
{
      Mercury 
= 1,
      Venus 
= 2,
      Earth 
= 3,
      Mars 
= 4,
      Jupiter 
= 5,
      Saturn 
= 6,
      Neptune 
= 7,
      Uranus 
= 8,
      Pluto 
= 9
}


Planet sphere 
= new Planet();

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

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

Planet sphere = Planet.Mars;

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

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

ObservationData d = new ObservationData();

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

public enum Planet
{
      None 
= 0,
      Mercury 
= 1,
      Venus 
= 2,
      Earth 
= 3,
      Mars 
= 4,
      Jupiter 
= 5,
      Saturn 
= 6,
      Neptune 
= 7,
      Uranus 
= 8,
      Pluto 
= 9
}


Planet sphere 
= new Planet();

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

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

[Flags]
public enum Styles
{
      None 
= 0,
      Flat 
= 1,
      Sunken 
= 2,
      Raised 
= 4
}

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

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

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

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

public struct LogMessage
{
      
private int _ErrLevel;
      
private string _msg;
}


LogMessage MyMessage 
= new LogMessage();

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

public struct LogMessage
{
      
private int _ErrLevel;
      
private string _msg;

      
public string Message
      
{
            
get
            
{
                  
return(_msg != null)? _msg:string.Empty;
            }

            
set
            
{
                  _msg 
= value;
            }

      }

}

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

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

      回到目录

posted on 2006-09-16 12:12  aiya  阅读(1236)  评论(5编辑  收藏  举报