前段时间在某篇博文中与园友就异常的问题进行过激烈的讨论,看我的博客就知道我是个异常的支持者,但看到非常非常多的人不喜欢异常,因此还来向大家请教一下这个问题
现在对于异常通常有2种理论:
1.异常非常地消耗资源,因此能不用就不用,不要用异常控制流程,也不要随便抛出异常
2.异常是面向对象中非常重要的概念,用于对错误的统一通知,因此有错误时应该抛出异常
假设有以下方法

Code
public class User
{
public int ID { get; set; } //自增
public string Name { get; set; }
}
public class UserLogic
{
public User InsertUser(User user)
{
//
}
}
在代码中,由于User的ID为自增列,因此在Insert之后为了获取数据库自动生成的ID,InsertUser必须返回一个User的对象(也许在本地调用的时候可以自用引用传值来获取ID,但是在分布式环境下必须这样设计)
对于上面提到的2种异常观点的第1种观点,他们认为InsertUser这个方法完全不应该抛出异常,其实现可能如此

Code
if (user == null) return null;
if (user.Name == null) return null;
string sql = "";
try
{
SqlHelper.ExecuteNonQuery(sql); //执行SQL
}
catch (SqlException)
{
return null;
}
而对于第2种观点,则认为应该由异常通知具体错误,实现可能如下

Code
if (user == null)
throw new ArgumentNullException("用户不能为null");
if (user.Name == null)
throw new ArgumentException("用户名不能为空");
string sql = "
";
try
{
SqlHelper.ExecuteNonQuery(sql);
}
catch (SqlException ex)
{
throw new InvalidOperationException(ex.Message);
}
在这个方法中为了不让上层感知到SqlException的存在,特意catch了SqlException并改为InvalidOperationException,使得封装性更强
从基本的实现可以看出,不使用异常的情况下,没有办法得知传入的user到底有什么问题,假设User类还有Age, Address等字段,仅仅依靠返回值根本无法判断哪一个字段有问题
而使用异常自然要消耗一定的资源,但却可以非常明确地告知调用者出错的原因
持第1种意见的人往往又认为,可以包装返回值来实现错误的通知,这时他们往往会将方法签名改掉

Code
public class InsertUserResult
{
public string Message { get; set; }
public bool IsSuccessful { get; set; }
public User Result { get; set; }
}
public InsertUserResult InsertUesr(User user);
但是这样做的问题有:
1.Message依旧无法很好地告知哪一个字段出问题,客户端无法根据Message来订制错误信息,除非调用者与设计者就Message属性的取值有事先的约定,但是设计并不应该依赖于这种约定
2.也许每一个方法都需要一个XxxResult类来封装,也许可以使用泛型生成一个ExecuteResult<T>的类,但是这种
封装已经完全脱离了业务逻辑的核心,使得接口的设计并没有体现出业务的逻辑,对一开始的建模产生了影响,同时接口也不够优雅、美观
个人认为,异常不应该因为其在性能上带来的损失而被放弃,异常真正的优势在于提供一个统一的错误通知的方案,而这个优势根本不是其他设计能够弥补的,不知大家对一这个问题有没有什么想法,到底是不是应该在这种情况下使用异常,应该如何使用,对于异常的使用又有什么准则?