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

翻译 2008年10月03日 20:45:00

Item8  Ensure That 0 Is a Valid State for Value Types

对于值类型,确保0是一个有效的状态

The default .NET system initialization sets all objects to all 0s. There is no way for you to prevent other programmers from creating an instance of a value type that is initialized to all 0s. Make that the default value for your type.

默认的.Net系统初始化机制将所有的对象都设置为0。对于你来说,没有办法阻止其他程序员来创建一个被设置成全0的值类型的实例。将0定义为你的值类型的默认值。

One special case is enums. Never create an enum that does not include 0 as a valid choice. All enums are derived from System.ValueType. The values for the enumeration start at 0, but you can modify that behavior:

一个特殊的情况是枚举(enums),不要创建没把0做为有效选择的enums。所有的枚举类型都派生自System.ValueType。枚举类型的值是从0开始的,但是它可以被修改:

  1.     public enum Planet
  2.     {
  3.         //Explicitly assign values 显式赋值
  4.         //Default starts at 0 otherwise 否则将默认从0开始
  5.         Mercury = 1,
  6.         Venus = 2,
  7.         Earth = 3,
  8.         Mars = 4,
  9.         Jupiter = 5,
  10.         Saturn = 6,
  11.         Neptune = 7,
  12.         Uranus = 8,
  13.         Pluto = 9
  14.     }
  15.  
  16. Planet sphere = new Planet();

sphere is 0, which is not a valid value. Any code that relies on the (normal) fact that enums are restricted to the defined set of enumerated values won't work. When you create your own values for an enum, make sure that 0 is one of them. If you use bit patterns in your enum, define 0 to be the absence of all the other properties.

Sphere 0,并非是一个有效值。任何依赖于下面事实(通常都是这样的情况)的代码都不能工作:枚举值被限制在预定义的枚举值集合内。因此,当你为一个枚举创建自己的值的时候,确定0是其中的一个。如果在自己的枚举类型中使用位模式,将0定义为所有其他属性之外的情况。

As it stands now, you force all users to explicitly initialize the value:

根据目前的情况来看,你强迫所有的用户需要显式的初始化这个值:

  1. Planet sphere = Planet.Mars;

That makes it harder to build other value types that contain this type:

这使创建其它包含该类型的值类型变得很困难:

  1.     public struct ObservationData
  2.     {
  3.         Planet whichPlanet; //what am I looking at?
  4.         Double magnitude; // perceived brightness.
  5.     }

Users who create a new ObservationData object will create an invalid Planet field:

创建新ObservationData对象的用户将创建一个无效的Planet字段:

  1. ObservationData d = new ObservationData();

The newly created ObservationData has a 0 magnitude, which is reasonable. But the planet is invalid. You need to make 0 a valid state. If possible, pick the best default as the value 0. The Planet enum does not have an obvious default. It doesn't make any sense to pick some arbitrary planet whenever the user does not. When you run into that situation, use the 0 case for an uninitialized value that can be updated later:

新创建的ObservationData拥有一个为0magnitude,这是合理的。但是planet是无效的。你必须使0是一个有效的状态。如果可能的话,将默认的情况最好设置为0Planet枚举没有一个明显的默认值。当用户没有指定的时候就随便使用一些任意星球是说不过去的。当你遇到这种情况的时候,将0定义为一个未初始化的值,使得以后可以更新。

  1.     public enum Planet
  2.     {
  3.         None = 0,
  4.         Mercury = 1,
  5.         Venus = 2,
  6.         Earth = 3,
  7.         Mars = 4,
  8.         Jupiter = 5,
  9.         Saturn = 6,
  10.         Neptune = 7,
  11.         Uranus = 8,
  12.         Pluto = 9
  13.     }
  14. Planet sphere = new Planet();

sphere now contains a value for None. Adding this uninitialized default to the Planet enum ripples up to the ObservationData structure. Newly created ObservationData objects have a 0 magnitude and None for the target. Add an explicit constructor to let users of your type initialize all the fields explicitly:

Sphere现在包含一个None值。向Planet枚举中加入这个未初始化的默认值,使ObservationData结构起了变化。新创建的ObservationData对象,包含有一个0magnitude值,和一个NonewhichPlanet值。加入显式的构造器来让你的类型的用户可以显式的初始化所有的字段:

  1.     public struct ObservationData
  2.     {
  3.         Planet whichPlanet; //what am I looking at?
  4.         Double magnitude; // perceived brightness.
  5.  
  6.         ObservationData(Planet target,Double mag)
  7.         {
  8.             whichPlanet = target;
  9.             magnitude = mag;
  10.         }
  11.     }

But remember that the default constructor is still visible and part of the structure. Users can still create the system-initialized variant, and you can't stop them.

但是请记住,默认的构造器仍然是可见的,它是该结构的一部分。用户仍然可以创建由系统进行初始化的变量,而且你无法阻止。

Before leaving enums to discuss other value types, you need to understand a few special rules for enums used as flags. Enums that use the Flags attribute should always set the None value to 0:

在离开enums类型去讨论其他值类型之前,你需要明白对于enums具有一些特殊的规则,像作为位标记的Flags。使用Flags特性的enums应该始终将None值设置为0

  1.     [Flags]
  2.     public enum Styles
  3.     {
  4.         None = 0,
  5.         Flat = 1,
  6.         Sunken = 2,
  7.         Raised = 4,
  8.     }

Many developers use flags enumerations with the bitwise AND operator. 0 values cause serious problems with bitflags. The following test will never work if Flat has the value of 0:

多数开发人员将带有Flags特性的枚举和位操作符AND一起使用。在位标记上,0值会引起严重的问题。在下面的测试中,如果Flat具有0值,测试将永远不能工作:

  1.     if ((flag & Styles.Flat) != 0) // Never true if Flat == 0.
  2.         DoFlatThings();

If you use Flags, ensure that 0 is valid and that it means "the absence of all flags."

如果使用Flags,就要确认0是有效并表示“不包括所有其他标记的情况”。

Another common initialization problem involves valuetypes that contain references. Strings are a common example:

另外一种初始化问题涉及到包含有引用的值类型。String是一个常见的例子:

  1.     public struct LogMessage
  2.     {
  3.         private int errLevel;
  4.         private string msg;
  5.     }
  6.  
  7.    LogMessage MyMessage = new LogMessage();

MyMessage contains a null reference in its _Msg field. There is no way to force a different initialization, but you can localize the problem using properties. You created a property to export the value of _Msg to all your clients. Add logic to that property to return the empty string instead of null:

MyMessagemsg字段上包含了一个空引用。没有办法强制一个不同的初始化,但是你可以使用属性来将该问题限制在类型内部。创建属性来向所有用户暴露msg的值,向该属性添加逻辑使其返回空字符串而不是null

  1.     public struct LogMessage
  2.     {
  3.         private Int32 errLevel;
  4.         private String msg;
  5.  
  6.         public String Message
  7.         {
  8.             get
  9.             {
  10.                 return (msg != null) ? msg : String.Empty;
  11.             }
  12.             set
  13.             {
  14.                 msg = value;
  15.             }
  16.         }
  17.     }

You should use this property inside your own type. Doing so localizes the null reference check to one location. The Message accessor is almost certainly inlined as well, when called from inside your assembly. You'll get efficient code and minimize errors.

应该在自己的类型内部使用该属性。这样做呢,可以让对null引用的检查集中在一个地方。当从程序集内部访问时,Message访问器几乎是内联的。这样,可以获得高效的代码和最小的错误。

The system initializes all instances of value types to 0. There is no way to prevent users from creating instances of value types that are all 0s. If possible, make the all 0 case the natural default. As a special case, enums used as flags should ensure that 0 is the absence of all flags.

系统将所有值类型的实例初始化为0。没有办法阻止用户创建全0的值类型实例。如果可能,就要将全0作为自然的默认值。作为一个特殊情况呢,被用作flagsenums应该保证0表示“不包括所有其他标记的情况”。

QSqlQuery::value: not positioned on a valid record 的错误的解决方法

QSqlQuery::value: not positioned on a valid record   在使用QSqlQuery查询数据并用query的结果value出现QSqlQuery...
  • u013652219
  • u013652219
  • 2014年09月07日 11:52
  • 4975

Mac下终端sudo命令错误的解决:sudo: effective uid is not 0, is sudo installed setuid root?

Mac下终端sudo命令错误的解决:sudo: effective uid is not 0, is sudo installed setuid root?
  • qingfeng14
  • qingfeng14
  • 2016年09月11日 20:09
  • 958

ERROR spi.SqlExceptionHelper : 'Infinity' is not a valid numeric or approximate numeric value 问题解决

ERROR spi.SqlExceptionHelper : 'Infinity' is not a valid numeric or approximate numeric value org.hi...
  • liyayunwxc
  • liyayunwxc
  • 2013年07月08日 15:38
  • 4756

'Operation is not valid due to the current state of the object' error during postback

根据客户需求,在一个页面上摆放了将近3000个textbox,没办法,客户是上帝啊。 提交之后,出现一个我没有看到过的error,Operation is not valid due to the ...
  • broze
  • broze
  • 2012年06月19日 10:37
  • 815

OCP-1Z0-053-V13.02-450题

450.Which of the following are valid program types for a lightweight job? (Choose all that apply.) ...
  • rlhua
  • rlhua
  • 2014年02月20日 20:06
  • 5558

qt关于not positioned on a valid record

qt关于not positioned on a valid record         在实现 query.value(0).toString()   语句的过程中,有时会出现not po...
  • llx523113241
  • llx523113241
  • 2014年12月10日 22:56
  • 3160

'artifactId' with value 'java' does not match a valid id pattern

编译项目报错: "C:\Program Files\Java\jdk1.7.0_80\bin\java" "-Dmaven.home=D:\Program Files\apache-maven-3.0...
  • jiangshubian
  • jiangshubian
  • 2017年04月09日 00:13
  • 946

DependencyProperty.UnsetValue}' is not a valid value for property 'Background' 的错误原因

查找了一下,发现是资源字典中一个资源(DynamicResource )值没有定义。记一下这个错误, 这个bug找了那么久,最终的解决让我啼笑皆非,原来是一个背景颜色的命名写错了...
  • zhang_xiaoyan
  • zhang_xiaoyan
  • 2013年04月28日 11:04
  • 638

'Infinity' is not a valid numeric or approximate numeric value 异常信息处理

sql中经常会出现如下所示的这种异常,Infinity 无穷大的意思, 所以造成的原因大部分是        除数为0    可能是接受的除数就是0,也可能是默认为0进行了除法运算,围绕这个方向去...
  • iphone4grf
  • iphone4grf
  • 2015年06月03日 11:03
  • 2153

Gson Builder — 特殊类型 Floats & Doubles

上一节介绍了 lenient 的使用方法,通过设置 lenient 属性,Gson 可以帮我忽略一些错误,保证解析尽量的匹配 Java 对象。本文将了解下 Gson 是如何解析特殊类型 Floats...
  • cpfdpzc
  • cpfdpzc
  • 2016年12月07日 18:31
  • 1111
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effective C#之8:Ensure That 0 Is a Valid State for Value Types
举报原因:
原因补充:

(最多只允许输入30个字)