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];
    }
}