声明:本文部分内容来源于《More Effective C#》
大家先设想一下这种情形,你是一个类的开发者,你想把该类的 API 提供给用户使用,假如说,你现在有一个 API DoWork(),这个 DoWork() 有可能会抛出异常,那么用户在使用你这个 API 的时候,他只能按照下面这种方法编程。
var worker = new MyWorker();
try
{
Worker.DoWork();
}
catch (Exception e)
{
ReportErrorToUser(“xxx error occurs, check xxxx.”)
}
这样编写存在三种弊端,首先,当用户捕捉到错误时,实际上他并不知道这个错误到底是 DoWork() 里面的哪个位置出的错,人家不是设计者嘛,不清楚你的内部逻辑是怎样的。第二,当你提供的 API 都有可能出现异常时,程序会出现大量的 try catch,这十分影响代码的整洁度和可读性。第三,try catch 对程序性能的影响比较严重。
我们在举一个简单的例子做对比。
实现一:
try
{
File.Open(“C:Codetest.txt”);
}
catch(Exception e)
{
Console.WriteLine(e);
}
实现二:
if(File.Exists())
{
File.Open(“C:Codetest.txt”);
}
else
{
Console.WriteLine("File doesn't exist");
}
我们发现,实现二无论在可读性上还是在错误捕捉的清晰性上都要高于实现一,用户显然知道错误发生的原因有可能是文件不存在,而不用依赖于异常信息来了解错误(有时异常信息甚至不能准确地反馈错误)。
因此养成一下的这种设计模式是一个非常好的习惯:
if(Class.ConditionA() || Class.ConditionB() || Class.ConditionC())
{
Console.WriteLine("XXX Error");
return;
}
else if(Class.ConditionD())
{
Console.WriteLine("XXX Error");
return;
}
Class.DoWork();
而不是把逻辑装在若干个 try catch 里面。