我有一个应用程序,我使用了很多Exceptions
。
最近我听说Exceptions在性能方面成本很高。
我的应用程序看起来像这样:
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
}
这是不好的设计吗? 这会损害我的App性能吗?
答案 0 :(得分:1)
当抛出异常时,他们会重建堆栈跟踪。这非常昂贵。如果可以的话,尝试防止像这样的已知错误情况(这里是C风格):
public static const int INVERSE_DIVISION_BY_ZERO = -1; // any value not possible on correct operation will do
public int Inverse(int a)
{
if (a != 0)
{
return 1 / a;
}
return INVERSE_DIVISION_BY_ZERO;
}
现在,我知道这个例子很难看。它没什么用。
事实上,从测试开始:
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值,而不是。
现在这个例子在我看来是干净的可读代码,在将来修改Gender枚举时也是如此。至少它会告诉开发人员他忘记了什么......
另一个例子是:
// 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];
}
}
当values[i]
为0时, A 将失败,而 B 将忽略数组中的该位置。现在我想知道这个:
0
很少每1000年(真的)发生一次怎么办?这就像说从不,但基于概率估计。如果有可靠的数据表明错误情况非常罕见,则应该使用例外处理,因为测试的平均成本会更高。
这并不意味着你没有处理异常状态,它只是意味着你以一种非有效的方式处理它,因为它很少见,预先测试成本很高。
编辑:添加错误条件
编辑2:增加了一些想法
答案 1 :(得分:0)
如果要在某个时刻停止执行某个方法,则应使用return;
。
当您想要本地化一段代码出错的原因时,也会使用异常和异常捕获。通过找出发生的异常类型,您可以采取适当的措施。
答案 2 :(得分:0)
不,在-well- 异常状态下抛出异常是良好的设计。
另一方面,我看到过度使用异常的代码基本上将函数结果传回给调用者。 这个是糟糕的设计,很容易看出这些异常是不抛出异常状态,而是在常规操作和高频率下抛出。
答案 3 :(得分:0)
例外情况适用于例外个案。
例如,如果您要验证电子邮件地址,请编写电子邮件验证程序。
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
}
正如你所提到的,异常是昂贵的,因为它构建了一个巨大的堆栈跟踪,并且通常会停止整个程序。
正确使用例外的示例
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;
}
在这种情况下,我们抛出一个错误,让程序员知道他的代码中出错了。