【More Effective C#】掩藏在Nullable<T>后的秘密
2010-10-27 08:19 空逸云 阅读(2128) 评论(21) 编辑 收藏 举报对于可空类型,我们并不陌生.例如,当我们为我们的数据库生成一个实体时,例如LINQ2SQL,查看实体类中的字段,经常会发现.我们那些定义为可空的字段的类型后面都会多了个?,如int?string?char?,bool?等等.这就是今天的主角---可空类型.
Nullable<T>
事实上,所有的可空类型都继承自Nullable<T>类,而编译器为了方便大众,提供了几个常用的可空类型,可空基本类型.可空类型,顾名思义,它是可空的.这意味着,与非可空类型相比.可空类型需要更多的检查.可空类型为非可空类型添加了一种丢失或不可用的状态.日常项目中.可空类型似乎可以为我们带来很多的好处.
public static int? DefaultParse(this string input) { int answer; return int.TryParse(input, out answer) ? answer : default(int?); }
这个扩展方法看起来不会出什么错.实际上.它也能很好的运行.并很大程度上会优化我们的代码.但实际上.它也可能引入一些你可能不能预料到的问题.
Nullable<T>的运算
可空数值类型提供了类似浮点数和NaN之间的语义,所有涉及NaN的比较都将返回false.
double d = 0; Console.WriteLine(d > double.NaN); //false Console.WriteLine(d < double.NaN); //false Console.WriteLine(double.NaN > double.NaN); //false Console.WriteLine(double.NaN == double.NaN); //false
与NaN有区别的是.可空类型支持空值之间的等同性比较
int? nullableOne = default(int?); int? nullableTwo = 0; int? nullableThree = default(int?); Console.WriteLine(nullableOne < nullableTwo); //false Console.WriteLine(nullableOne > nullableTwo); //false Console.WriteLine(nullableOne == nullableThree); //true
涉及可空数值类型的操作与NaN数值的操作的行为完全相同.
double d = 0; Console.WriteLine(d + double.NaN); //NaN Console.WriteLine(d - double.NaN); //NaN Console.WriteLine(d * double.NaN); //NaN Console.WriteLine(d / double.NaN); //NaN int? nullableOne = default(int?); int? nullableTwo = default(int); Console.WriteLine((nullableOne + nullableTwo).HasValue); //false //....
Nullable<T>中提供了一个GetValueORDefault实现.可以获取相关可空类型的默认值.这样,我们必须频繁的调用GetValueORDefault,为了减轻我们的工作量与抱怨.减少我们砸键盘的次数.所以,C#中的??出现了.
??操作符
??操作符,也叫空值合并运算符.它是一个二元运算符.左边为可空类型,判读左边的值是否为空,若为空,则返回右边的值,否则返回左边的值.例如
int? result = 123; return result ?? 321; //return 123; int? result = default(int?); return result ?? 321; //return 321;
Nullable<T>信息丢失
在很多情况下.我们很Happy的使用着可空类型.但很多时候并难以理清某些特别情况下可空类型返回的结果.
int? f = default(int?); XmlSerializer x = new XmlSerializer(typeof(int?)); StringWriter t = new StringWriter(); x.Serialize(t, f);
这是一段序列化可空类型int的实现.生成的XML如下.
可以看到.类型是int,却没有内容.这样,我们很难看出这段代码其实是由int?类型序列化生成的.信息丢失了.并且在反序列化时,你将十分痛恨自己为什么要使用可空类型.
string storage = t.ToString(); StringReader s = new StringReader(storage); var f2 = (int)x.Deserialize(s); //f2不能为空 Console.WriteLine(f2);
除了序列化操作会信息丢失,在类型转换时.也会发生信息的丢失.
int? defaultNullable = default(int?); string s = defaultNullable.ToString(); //转换成string的空内容""或string.Empty Console.Write(s); string s2 = ((object)defaultNullable).ToString(); //装箱后为null,ToString操作不能为空出错 Console.Write(s2);
最小化可空类型的可见范围
经过以上的"血泪史".我们终于可以看清Nullable<T>后面的一些小秘密.可空类型,如一把双刃剑,只有在更好的理解了它的一些特性之后.你才能将其使用得更加的得心应手.发挥其最大的效应.反之,它将给你带来无穷无尽的懊恼很悔恨(不断的咒骂着..该死的可空类型,该死的可空类型.).所以,最小化可空类型的可见范围(避免使用)将让我们的类型更易于使用,且不容易被误用.写出更高质量的代码.
出处:http://kongyiyun.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。