C# 使用异常结束函数是否不好?
我有一个应用程序,在那里我使用了大量的C# 使用异常结束函数是否不好?,c#,.net,exception,C#,.net,Exception,我有一个应用程序,在那里我使用了大量的异常 最近我听说异常在性能方面花费很大 我的应用程序如下所示: Try { Function1(); Function2(); Function3(); Function4(); } catch(Execption ex) { //I return to the user the error (ex) that happen inside the //function that fails } 我的每个函
异常
最近我听说异常在性能方面花费很大
我的应用程序如下所示:
Try
{
Function1();
Function2();
Function3();
Function4();
}
catch(Execption ex)
{
//I return to the user the error (ex) that happen inside the
//function that fails
}
我的每个函数1、2、3等看起来都类似于此
Void Function1()
{
//Do something...
//If something fail
Throw New Exception("Fails because bla bla");
//Do something...
//If something fail
Throw New Exception("Fails because bla bla bla bla");
//If everything its ok, just continue without throwing anything
}
这个设计不好吗?这会影响我的应用程序性能吗 您应该使用
return代码>当您希望在某个点停止方法的执行时
当您想本地化代码出错的原因时,可以使用异常和异常捕获。通过找出发生了什么类型的异常,您可以采取适当的措施。不,在异常状态上抛出异常是一种很好的设计
另一方面,我看到过过度使用异常的代码,基本上将函数结果传递回调用方。这是一种糟糕的设计,很容易看出这一点,因为这些异常不是在异常状态下抛出的,而是在正常操作期间以高频率抛出的。异常是针对异常情况的
例如,如果你想验证一个电子邮件地址,你需要编写一个电子邮件验证程序
public bool Validate(string emailAddr)
{
// Logic
return false; // validation failed.
}
然后在主逻辑中,您可以编写:
if(Validate(emailInpunt))
{
// Do stuff.
}
这样,您的代码变得非常可读
如果您使用异常,它将如下所示:
try
{
Validate(emailInpunt);
}
catch
{
// Something
}
在上面的示例中,代码更难阅读,如果您想添加不同的情况,那么控制流也会有点困难,fx。验证电子邮件,或者检查一些表示应该忽略它的值
此外,筑巢大量的试捕更难阅读
try
{
Validate(emailInpunt);
try
{
DoSomething(otherStuff);
}
catch
{
// Something
}
}
catch
{
// Something
}
正如您所提到的,异常是昂贵的,因为它构建了一个巨大的stacktrace,通常会停止整个程序
正确使用异常的示例
try
{
SqlConnection con = new SqlConnection(....);
// Do stuff with the database connection
}
catch(SqlException sex)
{
// Log this
throw new UserSomethingException("A connection to the database could not be established");
}
作为断言的异常
异常还用于告知开发人员编程错误
考虑这一点:
public double Calculate(MeaningOfLifeTheUniverseAndEverythingQuestion bigQuestion)
{
if(bigQuestion == null)
throw new ArgumentException("MeaningOfLifeTheUniverseAndEverythingQuestion cannot be null!!");
// 7½ million years of computing
return 42;
}
在本例中,我们抛出一个错误,让程序员知道他在代码中犯了一个错误。当抛出异常时,他们会重建堆栈跟踪等。这相当昂贵。如果可以,请尝试防止出现如下已知错误情况(此处为C-style):
我知道这个例子很难看。这帮不了什么忙
事实上,从测试开始:
if (a != 0)
{
b = Inverse(a);
}
现在是稍后测试的问题:
b = Inverse(a);
if (b != Inverse.INVERSE_DIVISION_BY_ZERO)
{
// safe to go
}
这有意义吗?不,这个例子很差。但它表明,为了保证代码执行的安全,需要进行的测试量是最小的。如果以前没有做过,就必须以后再做,但确实无法避免
对于异常的错误情况,应该抛出异常,因为在某种意义上,您无法像在驱动器中没有CD这样控制它们。当您已经知道代码如何失败时,请阻止它
我想补充一点:
异常处理是一个复杂的设计问题,而不仅仅是实现问题。它们是在这两个极端之间进行权衡:
- 干净、可读的代码,有时会抛出异常李>
- 高效的代码,很难读取,但可以防止异常并返回需要解释的C样式错误代码
常见的C#实践是支持干净、可读的代码,而不是过早的优化,我同意这一观点
但这并不意味着应该故意抛出异常。在有些情况下,故意投掷是完全有道理的,例如:
enum Gender
{
Unspecified,
Male,
Female
}
// later in the code
switch (gender)
{
case Gender.Unspecified:
// handle
break;
case Gender.Male:
// handle
break;
case Gender.Female:
// handle
break;
default:
throw new ArgumentException(string.Format("Unrecognized gender (was {0})", (int)gender));
}
未定义的性别无法呈现为字符串,因为它未定义。它将被呈现为int值
现在,我认为这个示例是干净可读的代码,在将来修改性别枚举时也很健壮。至少它会告诉开发者他忘了什么
另一个例子是:
// A: high performance, let it throw
for (i = 0; i < values.length; i++)
{
values[i] = 1 / values[i];
}
// B: slow performance, test first
for (i = 0; i < values.length; i++)
{
if (values[i] != 0)
{
values[i] = 1 / values[i];
}
}
/A:高性能,让它抛出
对于(i=0;i
当值[i]
为0时,A将失败,而B将忽略阵列中的该点。现在我想知道:
- 阵列对未处理的斑点有用吗?也许在那一点上它应该被扔掉李>
- 测试真的比抛出异常更有效吗
- 如果
0
很少发生,那么每1000年就会发生一次(真的吗)?这就像说永远不会,但基于概率估计
- 也许在每个周期中测试一些最可能不会发生的事情是一种浪费
如果有可靠的数据表明错误情况极为罕见,则应处理例外情况,因为测试的平均成本要高得多
这并不意味着您不处理异常状态,它只是意味着您以一种非有效的方式处理它,因为它很少见,而且事先测试成本很高
编辑:添加了错误条件
编辑2:增加了一些想法不要使用异常来控制程序流。线索在“异常”一词中。这是您可以在网上和Microsoft上轻松找到的信息@RicardoPolo异常是针对异常情况的,而不是针对普通的程序流。某个函数失败并抛出其中一个异常的概率是多少?如果低于2%(任意guestimate),则可以使用异常。如果是更多,那么例外可能是错误的。“例外,是针对例外情况的”。通常情况下,它们是致命的。@Vasile使用“return”;方法1结束,但方法2将继续。如果Method1失败,则使用异常的所有后续方法都将停止。你会怎么处理这件事?@MitchWheat偷了我的台词,嗯;)@米奇麦是的,当然,只是开玩笑。这是一个很常见的短语,也是范例的字面意思。很好的例子。您有一个return
语句。你要干什么
// A: high performance, let it throw
for (i = 0; i < values.length; i++)
{
values[i] = 1 / values[i];
}
// B: slow performance, test first
for (i = 0; i < values.length; i++)
{
if (values[i] != 0)
{
values[i] = 1 / values[i];
}
}