未将对象引用到实例,即NullReferenceException异常,我相信这是c#编程中最常见的错误之一,至少我在做项目的过程中,有很多时候都会抛出这个异常。每当这个异常出现的时候,我都会头皮一紧,因为以我的经验总结,一般情况下不会出现这个错误,但是一旦出现这个错误往往是比较难排查的,特别是代码量较大的时候,而等找到bug时,往往又会出现纠结状态,因为NullReferenceException异常经常和程序的逻辑紧密相连,这就意味着不是你的程序写错了,而很有可能是你的编程逻辑设计的不够合理。

当然,NullReferenceException异常的解决方案也非常的简单,只要在异常处加上类似下面的代码就可以了

 if(obj != null)
{
//some actions
}

意思就是在使用这个引用之前检查引用是否为空。但事实并非这么简单,正如上面说的,NullReferenceException异常往往和代码设计的逻辑是相关的,特别是代码量大的时候,一旦发现这个错误,往往会导致这一段代码的重构!

而且更重要的是,有时候在一段代码里,需要根据不同的情况来决定一个引用是否为null,或者是确实指向一个真实的对象。这种情况是最纠结的,因为这就意味着每个需要使用到这个引用的地方都要加上上述的代码!

作为一个程序员来说,这是不能容忍的!

因为一个简单的 if(obj != null) 的代码居然重复出现无数次,一篇代码里出现重复模式的代码,我认识这可以看做是程序员的失败!如何消除 if(obj != null)  这个模式呢?一些大牛曾经讲过:

任何时候都不应该使用null,你应该专门设计一个空类来代替null。

至于null指针的“罪与罚”,我想每个程序员都有自己的见解吧,在我看来,null指针可以列入编程史上最大的创新,同时也可列入最大的罪恶之源!

什么叫使用空类来代替null,这里就以日志Logger类作为示例来说明一下(当然,实际项目中你可以使用 log4net,或者其他IOC框架来实现日志记录)

 public class Logger
{
private class EmptyLog : Logger
{
public override void Log(string msg){ }
public override void LogErr(string errMsg) { }
public override void LogErr(Exception ex) { }
}
static Logger()
{
Empty = new EmptyLog();
}
public static Logger Empty { get; private set; } readonly TextWriter writer;
private Logger() { }
public Logger(TextWriter writer)
{
this.writer = writer;
}
public virtual void Log(string msg)
{
writer.WriteLine(DateTime.Now);
writer.WriteLine(msg);
writer.WriteLine();
}
public virtual void LogErr(string errMsg)
{
writer.WriteLine("error");
Log(errMsg);
}
public virtual void LogErr(Exception ex)
{
writer.WriteLine(string.Format("error<{0}>",ex.GetType().FullName));
Log(ex.Message);
}
}

下面是测试代码及测试结果:

 [STAThread]
static void Main()
{
var log = Logger.Empty;
log.Log("hellow world");
log.LogErr("error");
log.LogErr(new NullReferenceException());
log = new Logger(Console.Out);
log.Log("hellow world");
log.LogErr("this is a error message");
log.LogErr(new NullReferenceException());
}

可见,任何时候都不应该使用null,你应该专门设计一个空类来代替null,这句话的意思就是不要给一个引用赋值null,而是在初始化时要引用指向一个空类示例!

这种方法确实不错,至少是避免了 if(obj != null)  这种单调乏味的模式,但是我觉得还是有点不爽,因为那个空类纯粹就是一个资源浪费,既然不需要记录日志的地方,那就应该让引用指向null才对,表示什么都没有,而这种方法意味着即使你什么都不做,但是也要消耗一部分资源来供给这个空类使用!这种不必要的资源浪费和重复模式一样,都是我不能容忍的。于是我设计了一个方法:

利用扩展方法来避免  if(obj != null)  模式!

扩展方法Linq是我最喜欢的两个C#的语言特性,为什么Common Lisp那么强大,就是因为程序员可以自行对Lisp语法进行扩充,定制自己想要的语法,同样C#重点的扩展方法也可以实现扩展语法的功能,下面就是我用扩展方法重新设计的Logger类:

 public class Logger
{
readonly TextWriter writer;
public Logger(TextWriter writer)
{
this.writer = writer;
}
internal void Log(string msg)
{
writer.WriteLine(DateTime.Now);
writer.WriteLine(msg);
writer.WriteLine();
}
internal void LogErr(string errMsg)
{
writer.WriteLine("error");
Log(errMsg);
}
internal void LogErr(Exception ex)
{
writer.WriteLine(string.Format("error<{0}>",ex.GetType().FullName));
Log(ex.Message);
}
}
public static class LoggerHelper
{
public static void Log(this Logger logger, string msg)
{
if (logger != null)
logger.Log(msg);
}
public static void LogErr(this Logger logger, string errMsg)
{
if (logger != null)
logger.LogErr(errMsg);
}
public static void LogErr(this Logger logger, Exception ex)
{
if (logger != null)
logger.LogErr(ex);
}
}

下面是测试代码及测试结果:

 [STAThread]
static void Main()
{
Logger log = null;
log.Log("hellow world");
log.LogErr("error");
log.LogErr(new NullReferenceException());
log = new Logger(Console.Out);
log.Log("hellow world");
log.LogErr("this is a error message");
log.LogErr(new NullReferenceException());
}

从测试代码可以开出,即使变量log为null时,代码依然可以正常运行,因为我把 if(obj != null)  模式 利用扩展方法进行了封装,这样就可以放心的使用log了,不用担心log是否为null吗,而且在不需要记录日志的时候,直接将log赋值为null即可,不浪费任何资源。

这就是我关于解决“未将对象引用到实例”的方法,如果大家有其它好的办法,欢迎在这里进行交流!

让<未将对象引用到实例>见鬼去吧!的更多相关文章

  1. .NET[C#]中NullReferenceException(未将对象引用到实例)是什么问题?如何修复处理?(转)

    .NET[C#]中NullReferenceException(未将对象引用到实例)是什么问题?如何修复处理?  后端开发  作者: Rector  1973 阅读  0 评论  0 收藏  收藏本文 ...

  2. Asp.Net MVC 5 Razor 视图 未将对象引用到实例

    未将对象引用到实例的错误居然指向了@{Leyout=“..此处略,核实路径无误”}. 最后发现原来是在一个<select .. name="@Model.Category"& ...

  3. APIJSON,让接口见鬼去吧!

    我: APIJSON,让接口见鬼去吧! https://github.com/TommyLemon/APIJSON 服务端: 什么鬼? 客户端: APIJSON是啥? 我: APIJSON是一种JSO ...

  4. APIJSON,让接口和文档见鬼去吧!

    我: APIJSON,让接口和文档见鬼去吧! https://github.com/TommyLemon/APIJSON 服务端: 什么鬼? 客户端: APIJSON是啥? 我: APIJSON是一种 ...

  5. 【转】APIJSON,让接口见鬼去吧!

    我: APIJSON,让接口和文档见鬼去吧! https://github.com/TommyLemon/APIJSON 服务端: 什么鬼? 客户端: APIJSON是啥? 我: APIJSON是一种 ...

  6. 让你熟知jquery见鬼去吧

    $是jquery最具代表的符号,当然php也是,但是二者不能同日而语;不得不说jquery的选择器是大家赞不绝口的,在它1.x版本中对ie兼容性是最好的,这要归功于$选择器; 现在呢,html5的降临 ...

  7. Server.MapPath(string sFilePath) 报未将对象引用到实例异常

    System.Web.HttpContext.Current.Server.MapPath(string sfilePath)将虚拟路径转换成物理路径.这个必须在aspx或者MVC中Action调用才 ...

  8. VS 2015打开项目闪退,新建项目提示未将对象引用到实例

    因为开发需要,要把开发工具换成visual studio2015,装完之后会有警告“js”安装的问题,打开VS也没有问题, 但是一打开项目就闪退,新建项目也不行,查看应用程序日志,报错提示如下: .N ...

  9. HttpContext.Current.Server未将对象引用到实例

    问题描述: 在一些类库中需要读取当前系统的xml文件,当时用HttpContext.Current无法找到实例化对象 解决代码如下: XmlDocument xml = new XmlDocument ...

随机推荐

  1. merge into update

    如果要DML实现真正意义上的并发,在开始执行需要并发语句前,需要执行开启session并发  ALTER SESSION ENABLE PARALLEL DML;  在执行完语句后,需要执行关闭ses ...

  2. Spring(3.2.3) - Beans(8): 基于 Annotation 的配置

    除了基于 XML 的配置外,Spring 也支持基于 Annotation 的配置.Spring 提供以下介个 Annotation 来标注 Spring Bean: @Component:标注一个普 ...

  3. Git CMD - add: Add file contents to the index

    命令格式 git add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [- ...

  4. 移植FastBlur模糊算法至SDL

    FastBlur是Android标配的模糊算法,这也在当时引起了一股毛玻璃热潮.IOS7就采用了此算法(这有抄袭Android之嫌,因为Android1.5就在标库中加入了此函数).算法效率很高,这也 ...

  5. 直接拿来用,最火的.NET开源项目(beta)

    转自:http://blog.csdn.net/ltylove2007/article/details/18656971 综合类 微软企业库 微软官方出品,是为了协助开发商解决企业级应用开发过程中所面 ...

  6. 工具:linux 性能监控工具-nmon

    nmon 下载:http://pan.baidu.com/s/1jICoSpo 放到linux下可以直接运行. nmon 工具可以帮助在一个屏幕上显示所有重要的性能优化信息,并动态地对其进行更新. n ...

  7. CVTE面试总结

    刚面完了CVTE的一面,感觉完败,现总结. 一:准备不充分 这个跟时间仓促也有关系.昨天下午收到通知,晚上九点多回来填写给定格式的简历,题目也比较多.由于时间较仓促,那些问题的答案没有过多斟酌.但糟糕 ...

  8. MySQL的 inner join on 与 left join on

    WHERE子句中使用的连接语句,在数据库语言中,被称为隐性连接.INNER JOIN……ON子句产生的连接称为显性连接. inner join:理解为“有效连接”,两张表中都有的数据才会显示left ...

  9. Call与Apply

    1.前言 ECMAscript中提供了两个方法(call,apply)用于改变对象内部的this指针,它们两个的作用都是一样的,但是传递的参数有点不大相同. 它们的大概语法为: call(this, ...

  10. Mac OS X安装OpenCV 3.1.0

    在我的上一篇文章“”中已经介绍了Linux下OpenCV的安装配置方法,在这里仅仅记录Mac上相对于Linux的一点点差异. 1. 安装依赖包 Mac上安装软件包使用的工具是brew,用此来替代Ubu ...