解析C#中断言与异常的应用方式及异常处理的流程控制

2019-12-26 16:59:43王冬梅

现在再来看看断言,我们还是以下面的一段代码为例子。


public Entities.SimpleBugInfo GetSimpleBugInfo(string bugNum)
    {

      var selector = DependencyFactory.Resolve<ISelector>();

      var list = selector.Return<Entities.SimpleBugInfo>(
        reader => new Entities.SimpleBugInfo
        {
          bugNum = reader["bugNum"].ToString(),
          dealMan = reader["dealMan"].ToString(),
          description = reader["description"].ToString(),
          size = Convert.ToInt32(reader["size"]),
          fired = Convert.ToInt32(reader["fired"]),
        },
        "select * from bugInfo",
        new WhereClause(bugNum, "bugNum"));

      Trace.Assert(list != null);
      
      if (list.Count == 0)
        return null;
      else
        return list[0];

    }

当我贴出这段代码时,心情有些坎坷,因为我本人在这里也纠结了很久,这也是我一直没有将断言和异常划清界线的原因之一。

首先我们来回顾一下之前定义的断言使用场景:内部不可能发生的错误。

selector.Return这段代码是不是内部代码?如果我们能够修改Return中的代码,说明它是内部代码;反之,说明它是外部代码。对于内部代码,我们可以用断言来保护其逻辑的不变性,当断言被触发时,我们就可以确信是内部代码的错误,我们应该立即修复。

再纠结一下,假设Return是外部代码,我们没有办法去修改它。那么上面的代码可以有两种写法(如果你有更多的想法,请赐教)。

第一种,直接抛出异常。


If(list == null)
{
  throw new NullReferenceException();
}

第二种,调整代码。


if(list == null || list.Count == 0)
{
  return null;
}
else
{
  return list[0];
}

当然,还有一种就是什么也不做,让代码执行下去直至系统为你抛出空引用错误。但这种做法违背了防卸性编程的原则,我们总是应行尽早或离错误的发生地最近的地方处理错误,避免错误数据流向系统的其它地方,产生更加严重的错误。

总结

对异常或断言的使用取决于你要防卸的是一个内部错误还是外部错误以及你认为它是一个内部错误或外部错误。如果你决定防卸一个内部错误,那请果断使用断言,反之,请使用异常。