网站备案流程以及所需资料,深圳福田华丰大厦网站建设,网站设计师培训学校,凡客诚品陈年说在#xff0c;开篇之前 null、nullable、??运算符、null object模式#xff0c;这些闪亮的概念在你眼前晃动#xff0c;我们有理由相信“存在即合理”#xff0c;事实上#xff0c;null不光合理#xff0c;而且重要。本文#xff0c;从null的基本认知开始#xff0… 说在开篇之前 null、nullable、??运算符、null object模式这些闪亮的概念在你眼前晃动我们有理由相信“存在即合理”事实上null不光合理而且重要。本文从null的基本认知开始逐层了解可空类型、??运算符和null object模式在循序之旅中了解不一样的null。 你必须知道的.NET继续全新体验分享更多色彩。 www.anytao.com 1 从什么是null开始 null一个值得尊敬的数据标识。 一般说来null表示空类型也就是表示什么都没有但是“什么都没有”并不意味“什么都不是”。实际上null是如此的重要以致于在JavaScript中Null类型就作为5种基本的原始类型之一与Undefined、Boolean、Number和String并驾齐驱。这种重要性同样表现在.NET中但是一定要澄清的是null并不等同于0string.Empty这些通常意义上的“零”值概念。相反null具有实实在在的意义这个意义就是用于标识变量引用的一种状态这种状态表示没有引用任何对象实例也就是表示“什么都没有”既不是Object实例也不是User实例而是一个空引用而已。 在上述让我都拗口抓狂的表述中其实中心思想就是澄清一个关于null意义的无力诉说而在.NET中null又有什么实际的意义呢 在.NET中null表示一个对象引用是无效的。作为引用类型变量的默认值null是针对指针引用而言的它是引用类型变量的专属概念表示一个引用类型变量声明但未初始化的状态例如 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffobject/span obj span stylecolor:#0000ffnull/span;/span/span 此时obj仅仅是一个保存在线程栈上的引用指针不代表任何意义obj未指向任何有效实例而被默认初始化为null。 object obj和object obj null的区别 那么object obj和object obj null有实际的区别吗答案是有。主要体现在编译器的检查上。默认情况下创建一个引用类型变量时CLR即将其初始化为null表示不指向任何有效实例所以本质上二者表示了相同的意义但是有有所区别 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#008000//编译器检测错误使用未赋值变量obj/span/span/span //object obj; span stylecolor:blackspan stylecolor:black /span/span //编译器理解为执行了初始化操作所以不引发编译时错误 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffobject/span obj span stylecolor:#0000ffnull/span;/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffif/span (obj span stylecolor:#0000ffnull/span)/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#008000//运行时抛出NullReferenceException异常/span/span/span Console.WriteLine(obj.ToString()); span stylecolor:blackspan stylecolor:black }/span/span 注当我把这个问题抛给几个朋友时对此的想法都未形成统一的共识几位同志各有各的理解也各有个的道理。当然我也慎重的对此进行了一番探讨和分析但是并未形成完全100%确定性的答案。不过在理解上我更倾向于自己的分析和判断所以在给出上述结论的基础上也将这个小小的思考留给大家来探讨好的思考和分析别忘了留给大家。事实上将 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffstatic/span span stylecolor:#0000ffvoid/span Main(span stylecolor:#0000ffstring/span[] args)/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffobject/span o;/span/span object obj null; span stylecolor:blackspan stylecolor:black }/span/span 反编译为IL时二者在IL层还是存在一定的差别 span stylecolor:blackspan stylecolor:black.method span stylecolor:#0000ffprivate/span hidebysig span stylecolor:#0000ffstatic/span span stylecolor:#0000ffvoid/span Main(span stylecolor:#0000ffstring/span[] args) cil managed/span/span { span stylecolor:blackspan stylecolor:black .entrypoint/span/span .maxstack 1 span stylecolor:blackspan stylecolor:black .locals init (/span/span [0] object o, span stylecolor:blackspan stylecolor:black [1] span stylecolor:#0000ffobject/span obj)/span/span L_0000: nop span stylecolor:blackspan stylecolor:black L_0001: ldnull /span/span L_0002: stloc.1 span stylecolor:blackspan stylecolor:black L_0003: ret /span/span } 前者没有发生任何附加操作而后者通过ldnull指令推进一个空引用给evaluation stack而stloc则将空引用保存。 回到规则 在.NET中对null有如下的基本规则和应用 null为引用类型变量的默认值为引用类型的概念范畴。null不等同于0string.Empty。引用is或as模式对类型进行判断或转换时需要做进一步的null判断。 快捷参考 关于is和as模式可以参考《你必须知道的.NET》 7.5节“恩怨情仇is和as”第一回恩怨情仇is和as www.anytao.com 判断一个变量是否为null可以应用或!操作符来完成。对任何值为nul的l变量操作都会抛出NullReferenceException异常。 2 NullableT可空类型 一直以来null都是引用类型的特有产物对值类型进行null操作将在编译器抛出错误提示例如 span stylecolor:blackspan stylecolor:black span stylecolor:#008000//抛出编译时错误/span/span/span int i null; span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffif/span (i span stylecolor:#0000ffnull/span)/span/span { span stylecolor:blackspan stylecolor:black Console.WriteLine(span stylecolor:#006080i is null./span);/span/span } 正如示例中所示很多情况下作为开发人员我们更希望能够以统一的方式来处理同时也希望能够解决实际业务需求中对于“值”也可以为“空”这一实际情况的映射。因此自.NET 2.0以来这一特权被新的System.NullableT即可空值类型的诞生而打破解除上述诟病可以很容易以下面的方式被实现 span stylecolor:blackspan stylecolor:black span stylecolor:#008000//NullableT解决了这一问题/span/span/span int? i null; span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffif/span (i span stylecolor:#0000ffnull/span)/span/span { span stylecolor:blackspan stylecolor:black Console.WriteLine(span stylecolor:#006080i is null./span);/span/span } 你可能很奇怪上述示例中并没有任何Nullable的影子实际上这是C#的一个语法糖以下代码在本质上是完全等效的 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffint/span? i span stylecolor:#0000ffnull/span;/span/span Nullableint i null; 显然我们更中意以第一种简洁而优雅的方式来实现我们的代码但是在本质上NullableT和T?他们是一路货色。 可空类型的伟大意义在于通过NullableT类型.NET为值类型添加“可空性”例如NullableBoolean的值就包括了true、false和null而NullableInt32则表示值即可以为整形也可以为null。同时可空类型实现了统一的方式来处理值类型和引用类型的“空”值问题例如值类型也可以享有在运行时以NullReferenceException异常来处理。 另外可空类型是内置于CLR的所以它并非c#的独门绝技VB.NET中同样存在相同的概念。 Nullable的本质IL 那么我们如何来认识Nullable的本质呢当你声明一个 span stylecolor:blackspan stylecolor:black NullableInt32 count span stylecolor:#0000ffnew/span NullableInt32();/span/span 时到底发生了什么样的过程呢我们首先来了解一下Nullable在.NET中的定义 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffstruct/span NullableT span stylecolor:#0000ffwhere/span T : span stylecolor:#0000ffstruct/span/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffprivate/span span stylecolor:#0000ffbool/span hasValue;/span/span internal T value; span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span Nullable(T span stylecolor:#0000ffvalue/span);/span/span public bool HasValue { get; } span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span T Value { get; }/span/span public T GetValueOrDefault(); span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span T GetValueOrDefault(T defaultValue);/span/span public override bool Equals(object other); span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffoverride/span span stylecolor:#0000ffint/span GetHashCode();/span/span public override string ToString(); span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffstatic/span span stylecolor:#0000ffimplicit/span span stylecolor:#0000ffoperator/span T?(T span stylecolor:#0000ffvalue/span);/span/span public static explicit operator T(T? value); span stylecolor:blackspan stylecolor:black }/span/span 根据上述定义可知Nullable本质上仍是一个struct为值类型其实例对象仍然分配在线程栈上。其中的value属性封装了具体的值类型NullableT进行初始化时将值类型赋给value可以从其构造函数获知 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span Nullable(T span stylecolor:#0000ffvalue/span)/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffthis/span.span stylecolor:#0000ffvalue/span span stylecolor:#0000ffvalue/span;/span/span this.hasValue true; span stylecolor:blackspan stylecolor:black }/span/span 同时NullableT实现相应的Equals、ToString、GetHashCode方法以及显式和隐式对原始值类型与可空类型的转换。因此在本质上Nullable可以看着是预定义的struct类型创建一个NullableT类型的IL表示可以非常清晰的提供例证例如创建一个值为int型可空类型过程其IL可以表示为 span stylecolor:blackspan stylecolor:black .method span stylecolor:#0000ffprivate/span hidebysig span stylecolor:#0000ffstatic/span span stylecolor:#0000ffvoid/span Main() cil managed/span/span { span stylecolor:blackspan stylecolor:black .entrypoint/span/span .maxstack 2 span stylecolor:blackspan stylecolor:black .locals init (/span/span [0] valuetype [mscorlib]System.Nullable1int32 a) span stylecolor:blackspan stylecolor:black L_0000: nop /span/span L_0001: ldloca.s a span stylecolor:blackspan stylecolor:black L_0003: ldc.i4 0x3e8/span/span L_0008: call instance void [mscorlib]System.Nullable1int32::.ctor(!0) span stylecolor:blackspan stylecolor:black L_000d: nop /span/span L_000e: ret span stylecolor:blackspan stylecolor:black }/span/span 对于可空类型同样需要必要的小结 可空类型表示值为null的值类型。不允许使用嵌套的可空类型例如NullableNullableT 。NullableT和T?是等效的。对可空类型执行GetType方法将返回类型T而不是NullableT。c#允许在可空类型上执行转换和转型例如 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffint/span? a 100;/span/span Int32 b (Int32)a; span stylecolor:blackspan stylecolor:black a span stylecolor:#0000ffnull/span;/span/span 同时为了更好的将可空类型于原有的类型系统进行兼容CLR提供了对可空类型装箱和拆箱的支持。 3 ??运算符 在实际的程序开发中为了有效避免发生异常情况进行null判定是经常发生的事情例如对于任意对象执行ToString()操作都应该进行必要的null检查以免发生不必要的异常提示我们常常是这样实现的 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffobject/span obj span stylecolor:#0000ffnew/span span stylecolor:#0000ffobject/span();/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffstring/span objName span stylecolor:#0000ffstring/span.Empty;/span/span if (obj ! null) span stylecolor:blackspan stylecolor:black {/span/span objName obj.ToString(); span stylecolor:blackspan stylecolor:black }/span/span span stylecolor:blackspan stylecolor:black Console.WriteLine(objName);/span/span 然而这种实现实在是令人作呕满篇的if语句总是让人看着浑身不适那么还有更好的实现方式吗我们可以尝试? :三元运算符 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffobject/span obj span stylecolor:#0000ffnew/span span stylecolor:#0000ffobject/span();/span/span string objName obj null ? string.Empty : obj.ToString(); span stylecolor:blackspan stylecolor:black Console.WriteLine(objName);/span/span 上述obj可以代表任意的自定义类型对象你可以通过覆写ToString方法来输出你想要输出的结果因为上述实现是如此的频繁所以.NET 3.0中提供了新的操作运算符来简化null值的判断过程这就是??运算符。上述过程可以以更加震撼的代码表现为 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffobject/span obj span stylecolor:#0000ffnull/span;/span/span string objName (obj ?? string.Empty).ToString(); span stylecolor:blackspan stylecolor:black Console.WriteLine(objName);/span/span 那么??运算符的具体作用是什么呢 ??运算符又称为null-coalescing operator如果左侧操作数为null则返回右侧操作数的值 如果不为null则返回左侧操作数的值。它既可以应用于可空类型有可以应用于引用类型。 插播广告我的新书 4 Nulll Object模式 模式之于设计正如秘笈之于功夫。正如我们前文所述null在程序设计中具有举足轻重的作用因此如何更优雅的处理“对象为空”这一普遍问题大师们提出了Null Object Pattern概念也就是我们常说的Null Object模式。例如Bob大叔在《敏捷软件开发--原则、模式、实践》一书Martin Fowler在《Refactoring: Improving the Design of Existing Code》一书都曾就Null Object模式展开详细的讨论可见23中模式之外还是有很多设计精髓可能称为模式有碍经典。但是仍然值得我们挖据、探索和发现。 下面就趁热打铁在null认识的基础上对null object模式进行一点探讨研究null object解决的问题并提出通用的null object应用方式。 解决什么问题 简单来说null object模式就是为对象提供一个指定的类型来代替对象为空的情况。说白了就是解决对象为空的情况提供对象“什么也不做”的行为这种方式看似无聊但却是很聪明的解决之道。举例来说一个User类型对象user需要在系统中进行操作那么典型的操作方式是 span stylecolor:blackspan stylecolor:blackspan stylecolor:black span stylecolor:#0000ffif/span (user ! span stylecolor:#0000ffnull/span)/span/span/span { span stylecolor:blackspan stylecolor:blackspan stylecolor:black manager.SendMessage(user);/span/span/span } 这种类似的操作会遍布于你的系统代码无数的if判断让优雅远离了你的代码如果大意忘记null判断那么只有无情的异常伺候了。于是Null object模式就应运而生了对User类实现相同功能的NullUser类型就可以有效的避免繁琐的if和不必要的失误 span stylecolor:blackspan stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span/span span stylecolor:blackspan stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffclass/span NullUser : IUser/span/span/span { span stylecolor:blackspan stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffvoid/span Login()/span/span/span { span stylecolor:blackspan stylecolor:blackspan stylecolor:black span stylecolor:#008000//不做任何处理/span/span/span/span } span stylecolor:blackspan stylecolor:blackspan stylecolor:black /span/span/span public void GetInfo() { } span stylecolor:blackspan stylecolor:blackspan stylecolor:black /span/span/span public bool IsNull span stylecolor:blackspan stylecolor:blackspan stylecolor:black {/span/span/span get { return true; } span stylecolor:blackspan stylecolor:blackspan stylecolor:black }/span/span/span } IsNull属性用于提供统一判定null方式如果对象为NullUser实例那么IsNull一定是true的。 那么二者的差别体现在哪儿呢其实主要的思路就是将null value转换为null object把对user null这样的判断转换为user.IsNull虽然只有一字之差但是本质上是完全两回事儿。通过null object模式可以确保返回有效的对象而不是没有任何意义的null值。同时“在执行方法时返回null object而不是null值可以避免NullReferenceExecption异常的发生。”这是来自Scott Dorman的声音。 通用的null object方案 下面我们实现一种较为通用的null object模式方案并将其实现为具有.NET特色的null object所以我们采取实现.NET中INullable接口的方式来实现INullable接口是一个包括了IsNull属性的接口其定义为 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffinterface/span INullable/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Properties/span/span/span bool IsNull { get; } span stylecolor:blackspan stylecolor:black }/span/span 仍然以User类为例实现的方案可以表达为 图中仅仅列举了简单的几个方法或属性旨在达到说明思路的目的其中User的定义为 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffclass/span User : IUser/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffvoid/span Login()/span/span { span stylecolor:blackspan stylecolor:black Console.WriteLine(span stylecolor:#006080User Login now./span);/span/span } span stylecolor:blackspan stylecolor:black /span/span public void GetInfo() span stylecolor:blackspan stylecolor:black {/span/span Console.WriteLine(User Logout now.); span stylecolor:blackspan stylecolor:black }/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffbool/span IsNull/span/span { span stylecolor:blackspan stylecolor:black get { span stylecolor:#0000ffreturn/span span stylecolor:#0000fffalse/span; }/span/span } span stylecolor:blackspan stylecolor:black }/span/span 而对应的NullUser其定义为 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffclass/span NullUser : IUser/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffvoid/span Login()/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#008000//不做任何处理/span/span/span } span stylecolor:blackspan stylecolor:black /span/span public void GetInfo() { } span stylecolor:blackspan stylecolor:black /span/span public bool IsNull span stylecolor:blackspan stylecolor:black {/span/span get { return true; } span stylecolor:blackspan stylecolor:black }/span/span } 同时通过UserManager类来完成对User的操作和管理你很容易思考通过关联方式将IUser作为UserManger的属性来实现基于对null object的引入实现的方式可以为 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffclass/span UserManager/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffprivate/span IUser user span stylecolor:#0000ffnew/span User();/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span IUser User/span/span { span stylecolor:blackspan stylecolor:black get { span stylecolor:#0000ffreturn/span user; }/span/span set span stylecolor:blackspan stylecolor:black {/span/span user value ?? new NullUser(); span stylecolor:blackspan stylecolor:black }/span/span } span stylecolor:blackspan stylecolor:black }/span/span 当然有效的测试是必要的 span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffstatic/span span stylecolor:#0000ffvoid/span Main()/span/span { span stylecolor:blackspan stylecolor:black UserManager manager span stylecolor:#0000ffnew/span UserManager();/span/span //强制为null span stylecolor:blackspan stylecolor:black manager.User span stylecolor:#0000ffnull/span;/span/span //执行正常 span stylecolor:blackspan stylecolor:black manager.User.Login();/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffif/span (manager.User.IsNull)/span/span { span stylecolor:blackspan stylecolor:black Console.WriteLine(span stylecolor:#006080用户不存在请检查。/span);/span/span } span stylecolor:blackspan stylecolor:black }/span/span 通过强制将User属性实现为null在调用Login时仍然能够保证系统的稳定性有效避免对null的判定操作这至少可以让我们的系统少了很多不必要的判定代码。 详细的代码可以通过本文最后的下载空间进行下载。实际上可以通过引入Facotry Method模式来构建对于User和NullUser的创建工作这样就可以完全消除应用if进行判断的僵化不过那是另外一项工作罢了。 当然这只是null object的一种实现方案在此对《Refactoring》一书的示例进行改良完成更具有.NET特色的null object实现你也可以请NullUser继承Use并添加相应的IsNull判定属性来完成。 借力c# 3.0的Null object 在C# 3.0中Extension Method扩展方法对于成就LINQ居功至伟但是Extension Method的神奇远不是止于LINQ。在实际的设计中灵活而巧妙的应用同样可以给你的设计带来意想不到的震撼以上述User为例我们应用Extension Method来取巧实现更简洁IsNull判定代替实现INullable接口的方法而采用更简单的实现方式。重新构造一个实现相同功能的扩展方法例如 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffstatic/span span stylecolor:#0000ffclass/span UserExtension/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffstatic/span span stylecolor:#0000ffbool/span IsNull(span stylecolor:#0000ffthis/span User user)/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffreturn/span span stylecolor:#0000ffnull/span user;/span/span } span stylecolor:blackspan stylecolor:black }/span/span 当然这只是一个简单的思路仅仅将对null value的判断转换为null object的判断角度来看扩展方法带来了更有效的、更简洁的表现力。 null object模式的小结 有效解决对象为空的情况为值为null提供可靠保证。保证能够返回有效的默认值例如在一个IListUser userList中能够保证任何情况下都有有效值返回可以保证对userList操作的有效性例如 span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Copyright : www.anytao.com /span/span/span // Author : Anytaohttp://www.anytao.com span stylecolor:blackspan stylecolor:black span stylecolor:#008000// Release : 2008/07/31 1.0/span/span/span span stylecolor:blackspan stylecolor:black span stylecolor:#0000ffpublic/span span stylecolor:#0000ffvoid/span SendMessageAll(ListUser userList)/span/span { span stylecolor:blackspan stylecolor:black span stylecolor:#008000//不需要对userList进行null判断/span/span/span foreach (User user in userList) span stylecolor:blackspan stylecolor:black {/span/span user.SendMessage(); span stylecolor:blackspan stylecolor:black }/span/span } 提供统一判定的IsNull属性。可以通过实现INullable接口也可以通过Extension Method实现IsNull判定方法。null object要保持原object的所有成员的不变性所以我们常常将其实现为Sigleton模式。Scott Doman说“在执行方法时返回null object而不是null值可以避免NullReferenceExecption异常的发生”这完全是对的。5 结论 虽然形色匆匆但是通过本文你可以基本了解关于null这个话题的方方面面堆积到一起就是对一个概念清晰的把握和探讨。技术的魅力大概也正是如此而已吧色彩斑斓的世界里即便是“什么都没有”的null在我看来依然有很多很多。。。值得探索、思考和分享。 还有更多的null例如LINQ中的nullSQL中的null仍然可以进行探讨我们将这种思考继续所收获的果实就越多。 Anytao | 2008-07-31 | 你必须知道的.NET http://www.anytao.com/ | Blog: http://anytao.cnblogs.com/ | Anytao原创作品转贴请注明作者和出处留此信息。 参考文献 BookMartin FowlerRefactoring: Improving the Design of Existing Code cnblogszhuweisky使用Null Object设计模式 blogsScott DormanNull Object pattern 温故知新 [开篇有益] [第一回恩怨情仇is和as] [第二回对抽象编程接口和抽象类] [第三回历史纠葛特性和属性] [第四回后来居上class和struct] [第五回深入浅出关键字---把new说透] [第六回深入浅出关键字---base和this] [第七回品味类型---从通用类型系统开始] [第八回品味类型---值类型与引用类型上内存有理] [第九回品味类型---值类型与引用类型中规则无边] [第十回品味类型---值类型与引用类型下应用征途] [第十一回参数之惑---传递的艺术上] [第十二回参数之惑---传递的艺术下] [第十三回从Hello, world开始认识IL] [第十四回认识IL代码---从开始到现在] [第十五回继承本质论] [第十六回深入浅出关键字---using全接触] [第十七回貌合神离覆写和重载] [第十八回对象创建始末上] [第十九回对象创建始末下] [第二十回学习方法论] 评论列表 #7楼 2008-07-31 08:38 荒芜 对于NullObject Pattern应该不会大量用到把. 我怎么感觉给类实现个INullable,有点不需要! 或者说我们判断.... null,未尝不可! 呵呵! 支持(0) 反对(0) #11楼 2008-07-31 08:45 菜菜灰 ?? .NET 2.0也存在的吧不是在.net 3.0才有的吧 支持(0) 反对(0) #12楼 2008-07-31 08:48 横刀天笑 荒芜 对于一个对自己的代码追求完美的程序员来说null object pattern非常漂亮。如果不使用null object pattern那么代码中到处会充斥着这样的代码 if(a ! null) { a.DoSomething(); } 注意是到处并不是一个地方出现。 还有别的地方的用处 第一个测试的时候测试驱动开发以测试先行。比如一个Blog对象你要对Blog进行测试先行那么你可以做一个IBlog接口然后先实现一个“假的”InvalidBlog对象进行测试。慢慢的添加功能最后迭代到你要实现的Blog。 第二个比如做用户验证的时候吧你可以做一个IUser接口一个InvalidUser,一个ValidUser当验证失败返回InvalidUser,验证成功返回ValidUser。而调用验证这一层的客户代码只需要面对IUser这个接口就可以了。 上面的可能和nullobject pattern的初衷有点背离不过也是nullobject pattern的演绎。看你怎么用了我觉得nullobject pattern简单而优美 支持(0) 反对(0) #30楼 2008-07-31 10:05 helloworld22 public void SendMessageAll(ListUser userList) { //不需要对userList进行null判断 foreach (User user in userList) { user.SendMessage(); } } 但是user.SendMessage()之前set user null还是会报错吧。 public void SendMessageAll(ListUser userList) { //不需要对userList进行null判断 foreach (User user in userList) { user null; user.SendMessage(); } } 支持(0) 反对(0) #32楼 2008-07-31 10:08 primeli 问下啊String s null 与 String s String.Empty 和 String s 这3个有什么不同 支持(0) 反对(0) #33楼 2008-07-31 10:19 阿德斯基 --引用-------------------------------------------------- 菜菜灰: ?? .NET 2.0也存在的吧不是在.net 3.0才有的吧 -------------------------------------------------------- 我测试了下 果然在.net2.0下成立 本人不才 头一回知道.net2.0下可以这样用 一直用一个耳朵的今天长见识了 支持(0) 反对(0) #34楼 2008-07-31 10:28 妖居 全面精辟 建议楼主再讲讲DBNull和null的关系。我为了做CRM系统特意写了个DBNullableT类来处理DBNull和null的问题。不知道有没有更好的方法或者.NET已经提供了。 另外借地方说个VB中关于null的问题。IIf函数允许VB像C#一样使用三元运算符 ? : 第一个参数是一个返回Boolean的判断然后两个参数分别是为True和False的时候的返回值。但是和C#不同他的后两个参数是优先运算的。所以如果用IIf(user Is Nothing, String.Empty, user.ToString())则会出现运行时异常。 支持(0) 反对(0) #35楼 2008-07-31 10:29 Angel Lucifer 在我看来Nulll Object模式完全属于设计过度也没看出有多优雅。 如果你的项目使用类似的思想那可要质疑你的程序性能了。 支持(0) 反对(0) #36楼 2008-07-31 10:37 Angel Lucifer 非主流程序员 null 的含义楼主已经讲的很清楚了。 剩下的 String.Empty 呵呵。 支持(0) 反对(0) #38楼 2008-07-31 11:15 Duron800 --引用-------------------------------------------------- Angel Lucifer: 非主流程序员 null 的含义楼主已经讲的很清楚了。 剩下的 String.Empty quot;quot;呵呵。 -------------------------------------------------------- string.Empty好像不会提示never used 支持(0) 反对(0) #39楼 2008-07-31 11:16 Klesh Wong 这里的Null Object与直接判断null优雅在哪里 不过是把 object null 这个判断包装在另一个方法而已除了罗索和多一层调用看不出有什么优势啊。 Null Object不是这么用的吧不如举一下Logging的例子更好 Null Logging看起来就合理多了。 支持(0) 反对(0) #40楼 2008-07-31 11:38 横刀天笑 Angel Lucifer Klesh Wong 嗯我也觉得LZ这里的NullObject Pattern的例子没有选好这样写实在是有点设计过度不过NullObject Pattern确实很有用的null object不仅仅是一个null对象还可以理解为无效对象或者非法对象也就是表示一种对象状态所以程序里有这样的情况可以使用NullObject Pattern。 支持(0) 反对(0) #41楼 2008-07-31 11:52 怪怪 晕倒 原来你还在在意那天说的人家传过来的玩意是不是null的问题。 NullObject不见得一定就是设计过度 不过也不见得处处使用。 比如我自己 if(null xx || xx.length 0)这种代码已经打顺手了 -___- 如果嫌它丑 可以写一个静态的Guard类然后Guard.IsNull(obj)Guard.IsNullOrEmpty(obj)这样子。 主要是这些写法是一个明确的表达。 而NullObject尤其是你最后的那种Extension看起来代码虽然“优雅”但是在一些情况下 会把必要的信息隐藏了 从而存在不易察觉的隐患。 支持(0) 反对(0) #42楼 2008-07-31 12:00 Klesh Wong 横刀天笑 是这样子的Null Object当然是相当滴有用就比如说Logging,健壮的程序很多地方都是需要Logging的但在某些时候可能又不需要进行logging在每一次地方使用null判断是非常罗索的事情所以一个NullLogger会很有用NullLogger实行ILogger的行为但其实不做任何logging,这就是NullObject的美妙之处——使程序变得简洁明了。 但LZ的例子则恰好相反不但没有使程序更加简洁反而变得罗索。。。 支持(0) 反对(0) #43楼 2008-07-31 12:03 菜菜灰 回楼上2.0本身就提供这样一个方法 String.IsNullOrEmpty(string value) 支持(0) 反对(0) #44楼 2008-07-31 12:08 怪怪 菜菜灰 问题是你要防止NullOrEmpty的类型 不只string一种。 支持(0) 反对(0) 1 #46楼 2008-07-31 13:07 Klesh Wong 怪怪 那根本就不优雅原则上来说user.IsNull根本就违反直觉了吧。 如果user不存在则返回null或者直接抛出异常我想都是大家可以接受的行为。 相反你返回了一个非null的实例是不是就是暗示了user就是存在的最后还要调用它的IsNull来判定它本身是不是null这个逻辑上好像挺绕。就好像在问一个不存在的人“你存不存在”这样子。 静态类的判断也是很好的没有逻辑上的问题。就好像你问一个第三者这个人存不存在。直接判断null就相当于自己看看那个人存不存在。 支持(0) 反对(0) #47楼 [楼主] 2008-07-31 13:55 Anytao 荒芜 INullable只是在设计层面提供一个统一的契约由实现null object的类来遵守而关于以obj null方式进行判断很多时候还是无可避免因为你不可能给系统中所有的类实现相应的NullObject对应类同时很多临时变量还是无可避免的回到obj null #51楼 [楼主] 2008-07-31 14:08 Anytao 菜菜灰 应该是2.0和NullableT一起问世的我得再查查出处。谢谢提醒 支持(0) 反对(0) #52楼 [楼主] 2008-07-31 14:11 Anytao BZZ 呵呵还行啦 支持(0) 反对(0) #53楼 [楼主] 2008-07-31 14:16 Anytao 戏水 we are always nullable . 这句话我喜欢。 关于null的理解其实也不需要该深奥但是它确实就是“空”这个意思不知道是不是和“色既是空”有异曲同工之妙谁知道呢 支持(0) 反对(0) #54楼 [楼主] 2008-07-31 14:16 Anytao 侯垒 呵呵欢迎兄弟 支持(0) 反对(0) #55楼 [楼主] 2008-07-31 14:17 Anytao 蛙蛙池塘 谢谢啦感动的。。 支持(0) 反对(0) #56楼 [楼主] 2008-07-31 14:17 Anytao 真见 Thanks great 支持(0) 反对(0) #57楼 [楼主] 2008-07-31 14:18 Anytao jillzhang 呵呵眼泪哗哗的 支持(0) 反对(0) #58楼 [楼主] 2008-07-31 14:20 Anytao Artech 好久没有发帖了今天先打个头阵不过还没有什么大计划有了马上偷偷告诉你:-) Null 0二进制意义 Nice... 支持(0) 反对(0) #59楼 2008-07-31 15:12 信110 object o; object o null; 区别是第一个CLR为其赋默认NULL第二个CLR先为其赋默认NULL程序再显式赋为NULL最终效果没有区别。C#中所有变量使用前必须先赋值--这个赋值指的是程序的显式赋值所以第一个Write(o)是不行的第二个可以但在IL中两个都可以 .locals init (object v1) ldloc.0 call void [mscorlib]System.Console::Write(object) ------------------------------------------------ .locals init (object v1) ldnull stloc.0 ldloc.0 call void [mscorlib]System.Console::Write(object) 因为有init所以两个o在进入方法前都被CLR赋NULL了二进制的0。 值类型如何呢 int i; int i 0; 在C#中区别是明显的前者未赋值不能使后者赋值了能使。在IL中 .locals init (int32 v1) ldloc.0 call void [mscorlib]System.Console::Write(int32) ------------------------------------------ .locals init (int32 v1) ldc.i4.0 stloc.0 ldloc.0 call void [mscorlib]System.Console::Write(int32) --------------两个输出都是0可见第一个i CLR是给赋了值的 把第一个改改去掉init .locals (int32 v1) ldloc.0 call void [mscorlib]System.Console::Write(int32) -----------------输出10956832 是不是说明LCR没有给i赋值而是让它随便是什么。 有没有区别我也不清楚了 以上为个人理解不对请指出-_- 支持(0) 反对(0) #60楼 2008-07-31 16:13 999999999999999 很好好久不见你的文章了 支持(0) 反对(0) #61楼 [楼主] 2008-07-31 16:19 Anytao 姜敏 呵呵其实还有很多没有写例如Linq中的NullSQL中的Null只是限于精力没有继续以后吧:-) 支持(0) 反对(0) #62楼 [楼主] 2008-07-31 16:20 Anytao 丁学 横刀天笑 --引用-------------------------------------------------- 横刀天笑: 丁学 啥都没有就未知了俗话说武功最高境界就是无招见招拆招什么招都没有你还拆什么 呵呵开个玩笑 -------------------------------------------------------- 嘿嘿都有道理。 支持(0) 反对(0) #63楼 [楼主] 2008-07-31 16:21 Anytao Duron800 好欢迎回来看看:-) 支持(0) 反对(0) #64楼 2008-07-31 22:56 波波塔 ??这个运算符.我第一次看到.可能就是两周前吧,当时看MSDN里面的例子突然看到的.当时还以为是MSDN打错了....后来查了下帮助,第一眼看懂了它的大概用途,再看一眼MSDN,傻眼了...后面说的好像和前面的不一样.... 不知道有没有筒子和俺一样的哈.嘎嘎 另外楼主的那个 null object 例子,不是要对相应的非null object 实现空的方法体吗?那不是更麻烦? 支持(0) 反对(0) #65楼 [楼主] 2008-08-01 13:18 Anytao Tony Zhou public void SendMessageAll(ListIUser userList) { //不需要对userList进行null判断 foreach (IUser user in userList) { user null; user.SendMessage(); } } Is OK. 支持(0) 反对(0) #66楼 [楼主] 2008-08-01 13:19 Anytao 在职研究生 呵呵那是我的荣幸:-) 支持(0) 反对(0) #67楼 2008-08-01 13:22 陈晨 谢谢收益了 支持(0) 反对(0) #68楼 [楼主] 2008-08-01 13:30 Anytao --引用-------------------------------------------------- 非主流程序员: 问下啊String s null 与 String s String.Empty 和 String s 这3个有什么不同 -------------------------------------------------------- 非主流程序员 string s null和string.Empty的区别是前者在内存中保持的是一个空引用而后者则有实实在在的指向只是值为空而已对前者进行操作会引发NullReferenceException而后者不会。 String s String.Empty 和 String s 很多时候是可以互换的但是更推荐用String.Empty 支持(0) 反对(0) #69楼 [楼主] 2008-08-01 13:31 Anytao 探索之鸟 呵呵也是我的一个错误 支持(0) 反对(0) #70楼 [楼主] 2008-08-03 20:51 Anytao 妖居 呵呵首先VB.NET我不是很清楚所以关于Iif没有什么发言权啦:-) 本来本文最初想要涉及DBNull和Null in LINQ的但是最后限于时间和精力就没有成行。DBNull是.NET中预定义的处理数据库值为空的独立类型同时提供了很多有用的方法来支持其操作有机会可以在写个续什么的:-) 支持(0) 反对(0) #71楼 [楼主] 2008-08-03 21:35 Anytao Angel Lucifer 如果综合来看全文关于null的讲述我不知道所谓的过度体现在何处当然以obj null完全可以实现你所谓的非过度但是正如文中所提null object旨在解决一定的问题针对具体的需求而言。 另外关于性能的质疑我觉得大可不必因为其实很多设计都是以牺牲稍微的性能为代价的不然什么三层架构还有什么意义只有一层岂不更性能更高。 :-) 支持(0) 反对(0) #72楼 [楼主] 2008-08-03 21:42 Anytao Klesh Wong 关于这个示例我会考虑你的意见不过关于IsNull的判断其实在示例中我已经给出了应用你完全可以不必进行IsNull或者user null这样的判断而直接应用 public static void Main() { UserManager manager new UserManager(); //强制为null manager.User null; //执行正常 manager.User.Login(); } 所以关于文中示例可以全面的了解只关注 if (manager.User.IsNull) { Console.WriteLine(用户不存在请检查。); } 是没有意义的。 支持(0) 反对(0) #73楼 [楼主] 2008-08-03 21:45 Anytao 横刀天笑 很同意老兄的想法关于User实例我再看考虑考虑大家的意见不过正如我回复Klesh Wong的留言一样希望更多关注的是执行层面对于null object解决问题的分析情况。 支持(0) 反对(0) #74楼 [楼主] 2008-08-03 21:51 Anytao 怪怪 嘿嘿确实是那天的问题让我有时间思考这个话题所以进行一点小小的探讨。 言归正题关于null object我的想法并不局限于设计的过度和编码的习惯其实null object pattern有其自身解决问题的应用领域这点在文中其实不可避免但是很多时候你不可为所有的0bj null判断提供对应的null object类使其在null决议时执行值为null时的业务逻辑因为可能我的obj本身就是一个临时变量。 最后关于Extesion的解决思路呵呵我没有考虑太多“隐藏信息”只是当作一个小技巧在这里玩玩:-) 支持(0) 反对(0) #75楼 [楼主] 2008-08-03 22:02 Anytao Klesh Wong 首先关于user.IsNull是否违反直觉我觉得根本就没有什么直接而言正像怪怪所言更多的体现为一个习惯编码的习惯和直觉没有关系。 其次正如你反复以Logging举例我认为“什么对象”可以抽象为null object其实决定于具体的需求回到我的User实例。同样的道理健壮的程序很多地方也需要User的状态判断例如Login、Logout等等具体有什么样的需求决定于具体的业务逻辑这里只是一个简单的示例。因此同样的每个地方都进行null判断也是很啰嗦的所以以NullUser来实现对null判别的对象级判断可以有更好的代码体验和系统健壮性如果用户存在执行User.Login()登陆合法系统如果用户不存在执行NullUser.Login()转到错误页码或者其他的非法登陆处理逻辑。 而不管是User存在还是不存在在客户处理端是想到简洁和优雅 public static void Main() { UserManager manager new UserManager(); //强制为null manager.User null; //执行正常 manager.User.Login(); } 没有任何IsNull或者user null这样的判断同时能够保证系统执行的可靠性和健壮性。 和Logging是否是异曲同工 支持(0) 反对(0) #76楼 [楼主] 2008-08-03 22:05 Anytao 菜菜灰 看文知意null object和string.IsNullOrEmpty是两回事儿虽然有某种联系:-) 支持(0) 反对(0) #77楼 [楼主] 2008-08-03 22:06 Anytao 横刀天笑 这两天太忙了回复有点儿不及时不像我的风格。 哈哈随时沟通:-) 支持(0) 反对(0) #78楼 [楼主] 2008-08-03 22:08 Anytao 信110 很感谢你的详细分析呵呵和我的想法差不多不过在IL层还是不能100%的看出二者的差别不过我坚持代码优化之后在本质上二者是相同的。 支持(0) 反对(0) #79楼 [楼主] 2008-08-03 22:09 Anytao 北京奥运 呵呵有空就好好写写想法其乐无穷:-) 支持(0) 反对(0) #80楼 [楼主] 2008-08-03 22:11 Anytao airwolf2026 呵呵麻烦是为了换取优雅就像工厂方法模式为了创建对象还不是得付出附加的代码操作这是无可避免的代价:-) 不过在整个系统中你可以有效避免isNull或者user null这样的判断换来了简洁和健壮:-) 支持(0) 反对(0) #81楼 [楼主] 2008-08-03 22:11 Anytao 陈晨 哈哈一样一样:-) 支持(0) 反对(0) #82楼 2008-08-05 11:19 Klesh Wong Anytao User是否Login和表现层上下文有关跳转这些更加和具体表现层相关原则上User实体是不应该关心这些层次。Login/Logout这些状态应该由表现层的类如IPrincipal包装User才是这也是普遍做法。 NullObject应该是可以完全替代原类/接口的Dummy Instance也就是当客户端使用这个NullObject的时候应该是像使用一个类那样一个类怎么还会有IsNull这个的判定。它既然是实例怎么能判断自己是不是null。最重要的一点就是NullObject的使用者就是假定了得到的永远是实例而不会是null。 编程的习惯和直觉不是没有关系要是不喜欢直觉这个词用习惯来代替我所说的直觉也是一样的。 当你给某一个属性赋值后又马上调用它的方法这也是违反了习惯。OO是模拟现实中抽象出来的逻辑的编程方法代码的表述的逻辑有误这就是不符合OO精神的。 public static void Main() { UserManager manager new UserManager(); //强制为null manager.User null; //执行正常 manager.User.Login(); } 不可否认你的代码完全可以正常运行但你不觉得这样可读性太差了吗这样的代码你自己能看得懂但别人都会一头雾水吧。完全不觉得这样的代码是优雅的呢。 这段代码固然貌似就不用判断用户是不是Null。但其它地方一定还需要判定user是不是null比如说登录才显示某些信息之类这样的NullObject根本就没意义了吧之所谓使用NullObject就是它可以完全替代不是null时的情况而User作为“业务实例”没办法做完全替代。 支持(0) 反对(0) #83楼 2008-08-05 11:33 Klesh Wong --引用-------------------------------------------------- Anytao: Klesh Wong 关于这个示例我会考虑你的意见不过关于IsNull的判断其实在示例中我已经给出了应用你完全可以不必进行IsNull或者user null这样的判断而直接应用 public static void Main() { UserManager manager new UserManager(); //强制为null manager.User null; //执行正常 manager.User.Login(); } 所以关于文中示例可以全面的了解只关注 if (manager.User.IsNull) { Console.WriteLine(quot;用户不存在请检查。quot;); } 是没有意义的。 -------------------------------------------------------- 试问能在程序中完全避免这种判断的出现吗不能的话关注它又怎么会没有意义呢相反 这种判断应该还是到处都是。 即使不看这个还有其它许多问题LZ把很多表现层逻辑放到实体类中也不是太好违反SRP。 其实整篇都写得很好只是个人认为NullObject的例子举得不太恰当而已。 ??操作符也可以再讲多一个trick可以 a ?? b ?? c ?? d ?? e 这样…… 支持(0) 反对(0) #84楼 2008-08-12 17:03 Zzx飘遥 人帅文章也帅^_^ 支持(0) 反对(0) #85楼 [楼主] 2008-08-12 20:36 Anytao Klesh Wong 对于Wong兄上述描述给我不错的启示。不过我想自己的想法可能没有在文章中表达的特别清晰或者并没有收到预期的效果。关于NullObject完全可以替代原类的Dummy Instance这点是毋庸置疑的否则就失去了NullObject本身的意义也会违反设计原则的替换规则。 之所以将IsNull加入我的考虑仅仅是从提供一个Property这个观点上来看的不可否认很多时候还是需要以IsNull这样的形式来进行判断更加方便正像怪怪提出的习惯性问题。我完全同意将IsNull去掉会显得更加NullObject正像老兄表达的一样:-) 关于你提到的代码实例的种种不适例如 ---------------------------------- //强制为null manager.User null; //执行正常 manager.User.Login(); 的可读性 --------------- 把很多表现层逻辑放到实体类中也不是太好违反SRP ----------- 之所以有这样别扭的实现完全是因为在有限的篇幅来表达一个测试我不想花太多的代码或者你可以将它看成伪代码只是可以运行罢了:-) 再次感谢老兄中肯的提议让我了解很多难能可贵的思考欢迎常来赐教:-) 支持(0) 反对(0) #86楼 [楼主] 2008-08-12 20:36 Anytao 飘遥 哈哈见过人吗 支持(0) 反对(0) #87楼 2008-08-13 10:29 Klesh Wong Anytao 言重言重既是同道中人当可多多交流。 兄台肯好好考虑路人甲的意见也是难能可贵。 望能坚持有些人名气一大就目中无人呐根本不会去考虑别人的批评呵呵。 支持(0) 反对(0) #88楼 2008-08-13 13:00 Zzx飘遥 Anytao 见过植物园~ 支持(0) 反对(0) #89楼 [楼主] 2008-08-13 14:23 Anytao 飘遥 哈哈我还以为此飘遥非彼飘遥上次玩儿得很开心:-) 支持(0) 反对(0) #90楼 2009-01-21 17:20 痘痘熊 IsNullable这个词是否适合用来做Interface的名字呢参考系统中常用的interfaceIComparable, ICloneable, IEnumerable等等表达的都是某个本身并不一定具备的属性。而IsNullable是Class必然具备的属性就是说Class一定是IsNuulable的因此命名上可以再考虑考虑。 : ) 支持(0) 反对(0) #91楼 2009-01-27 22:36 刘晓飞 User.IsNull违反了OOD 支持(0) 反对(0) #92楼 [楼主] 2009-02-02 08:51 Anytao 痘痘熊 接口的名称是INullable而非IsNullable:-) 支持(0) 反对(0) #93楼 [楼主] 2009-02-02 08:55 Anytao 刘晓飞 这一争论已是个难断的公论了园子中也有不少辩论所以没法在此多言了对于OO而言我始终认为又是不必过度追求纯粹的100%的品质而灵活应用有时更重要:-) 支持(0) 反对(0) #94楼 2009-03-05 15:19 Sam Lin 我觉得null object没必要吧虽然客户端的代码优美了但会不会造成类爆炸呢 支持(0) 反对(0) #95楼 [楼主] 2009-03-05 20:53 Anytao Sam Lin 既然是“会不会”就有对应的“必要不必要”问题权衡没有标准此处只给出建议。 支持(0) 反对(0) #96楼 2011-07-12 11:49 testzhangsan 引用刘晓飞User.IsNull违反了OOD 是的我也觉得这会让维护变得痛苦。光一个 User.IsNull 就可以看出 User 一定不为 null 。 支持(0) 反对(0) #97楼 2011-11-24 15:01 Patrick Yu 正文的例子放在这里看来没有体现出应有的效果建议拿掉。不如专门开一篇讲这个更好不然限于篇幅而产生问题太可惜了更担心一些同学被误导而滥用这个模式。 支持(0) 反对(0) #98楼 2014-07-11 17:04 Tony二师弟 文章提到的的Null存在即意义我想了下这个Null Object模式的意义文中的User.Login()的例子确实没有说服力。所以我也想说这篇文章存在即意义。于是想到了一个例子可以使文章更具有说服力 伪代码 定义 public class Element { public Attribute Attr(string attrName); } public class Attribute { public string Value(); } 调用 Attribute eleelement.Attr(Name); var valuestring.Empty; if(ele!null) { valueele.Value(); } 引入NullObject模式后Attr()方法可以返回一个NullAttributeNullAttribute与文中的Login一样可以直接调用Value(),于是代码可以这样写 var valueelement.Attr(Name).Value(); 原来的四行代码变成现在的一行代码有木有想拥抱的感觉。 只是那个Login的例子有点不恰当。 支持(0) 反对(0)