C# 关于在何处放置Try-And-Catch语句的问题

C# 关于在何处放置Try-And-Catch语句的问题,c#,class-design,try-catch,C#,Class Design,Try Catch,我使用了try-and-catch语句作为一种简单的方法来保持代码运行而不会崩溃(我会用一次大的尝试来包装一切)。最近,我想开始更正确地使用try-and-catch语句。作为一个例子,我有以下问题: public class Ninja{ Ninja(){ } public void ThrowShirikin(int numberOfShirikins){ try{ if(numberOfShirikins == 0){

我使用了try-and-catch语句作为一种简单的方法来保持代码运行而不会崩溃(我会用一次大的尝试来包装一切)。最近,我想开始更正确地使用try-and-catch语句。作为一个例子,我有以下问题:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        try{
            if(numberOfShirikins == 0){
                throw new System.ArgumentException("Invalid number of shirikins");
            }

            //Throw shirikin
        }
        catch(ArgumentException e){
            MessageBox.Show(e.Message);
        }
    }
}
在上面的忍者类中,ThrowShirikin方法的全部内容都包含在一个try循环中。由于只有一次输入错误的机会(在本例中,当numberOfShirikins==0时),try循环中不应该只包含检查此错误的代码行吗?见下文:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        bool errorsExist = false;
        try{
            if(numberOfShirikins == 0){
                errorsExist = true;
                throw new System.ArgumentException("Invalid number of shirikins");
            }
        }
        catch(ArgumentException e){
            MessageBox.Show(e.Message);
        }

        if(!errorsExist){
            //Throw shirikin
        }
    }
}
^但我这里的东西似乎有点笨重。关于我如何理解try-catch语句的使用,有什么建议和意见吗?谢谢

编辑:

或者我可以这样做,以便在numberOfShirikins的值无效时//抛出shirikin代码永远不会执行:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        try{
            if(numberOfShirikins == 0){
                throw new System.ArgumentException("Invalid number of shirikins");
                return;
            }
        }
        catch(ArgumentException e){
            MessageBox.Show(e.Message);
        }

        //Throw shirikin
    }
}

当抛出异常(如
ArgumentException
时,您不应该在方法中捕获它们。为您的方法将此工作交给客户端。

看起来,try/catch的存在只是为了捕获您选择创建并抛出的异常,然后捕获它只是为了向用户弹出一个消息框。然后根据错误条件选择继续或不继续

消除
try
catch
。删除消息框。不要为无效参数引发异常。让函数的调用方确定是否要捕获以及如何处理。你的
忍者
职业不应该在识别哪些是有效的,哪些是无效的之外做出这些决定

if (numberOfShirikins == 0)
    throw new ArgumentException("...");

// rest of your shirikin throwing code

没有真正的理由用同样的方法捕获异常。没有:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        if(numberOfShirikins == 0){
            MessageBox.Show(e.Message);
            return;
        }

        //...
    }
}

您永远不应该以这种方式捕获异常。只捕获您可以实际处理的异常

公共级忍者{
忍者(){
}
公共无效ThrowShirikin(整数){

如果(numberOfShirikins一个方法不应该捕获它自己抛出的异常,那么生成它们是可以的,但是要理解它们是昂贵的,并且通常的流控制是首选的(这样更容易推理)

此外,在上面的例子中,errorsExist永远不会被设置为true-应该放在catch块中,而不是直接放在抛出异常的地方。这会终止正常的程序流

重申一下,这个方法是这样的

public void ThrowShirikin(int numberOfShirikins) {
    if(numberOfShirikins == 0){
        throw new System.ArgumentException("Invalid number of shirikins", "numberOfShirikins");
    }

    //Throw shirikin
}

由调用该方法的类决定如何处理异常。

这两种方法都不是一个好主意。在同一个方法中抛出异常并捕获它并显示该类的窗体对话框是没有意义的

只有应该包含GUI代码的类才是GUI中的类

在您的情况下,我也不会使用异常,我会使用布尔返回值:

public bool ThrowShirikin(int numberOfShirikins){
            if(numberOfShirikins == 0){
                return false;
            }
            //throw
            return true;
    }
例外情况应适用于例外情况,而不是一般情况


另外,请注意,第二种方式中的
errorsExist=true;
代码永远不会被调用,因为它位于throw语句下面。

IMO-异常实际上是代码中的异常情况-即无法预料的情况和/或错误可能在真正异常情况下出错的情况

如果上面显示的代码段have a
numberOfShirikins==0
不是异常情况,则它是正常的参数检查

因此,我将重新考虑代码:

public class Ninja{
    Ninja(){
    }

    public void ThrowShirikin(int numberOfShirikins){
        bool errorsExist = false;
        try{
            if(numberOfShirikins == 0){ showUserMessage("Invalid number of shirikins", MessageType.Information); }
        }
        catch(ArgumentException e){
            showUserMessage(e.Message, MessageType.Exception, e);
        }

        if(!errorsExist){
            //Throw shirikin
        }
    }
   private showUserMessage(string message, MessageType type, Exception ex = null){
        MessageBox.Show(message);
   }

}
完成后,您将发现捕捉异常实际上没有任何附加值,因此我们可以编写一个通用异常处理程序来显示异常&让您的代码正常运行

现在,我的代码如下所示:

public class Ninja{
        Ninja(){
        }

        public void ThrowShirikin(int numberOfShirikins){
                if(numberOfShirikins == 0){ showUserMessage("Invalid number of shirikins", MessageType.Information); return; }
                else{ /* do something here */ }
        }
另一方面,如果
numberOfShirikins==0
是一个异常情况,那么我将编写一个自定义异常,代码如下:

public class Ninja{
        Ninja(){
        }

        public void ThrowShirikin(int numberOfShirikins){
                if(numberOfShirikins == 0){ throw NumberOfShirikinsException(); }
                else{ /* do something here */ }
        }

当你抛出一个异常时,你基本上是在说:“这是一个我不知道如何处理的异常情况(或者更高层的另一个方法会更好地理解如何处理它)”

因此,你刚才发布的方法是:“当我被要求抛出0个shurikens时,我不知道该怎么办。实际上我撒谎了,我要显示一个消息框”


或者一起跳过异常并显示消息(不理想,但比抛出更好),或者只是抛出异常并在UI代码中更高级地捕获它。如果您想专门处理此特定异常,您可能需要创建自己的异常类型。

如果您可以避免抛出异常,您应该这样做。它们需要大约1000-4000个时钟周期。

您的errorsExist=true;将永远不会运行,因为throw命令将发送在catch中执行。因此errorsExist将始终为false。修复了它,感谢您指出这一点。+1抛出异常有助于调用堆栈在更高级别捕获错误。我明白了,所以我的方法抛出异常,这些方法的调用方将决定如何处理它们?就像我将调用ThrowShirikin(3)一样;然后试一试?@sooprise,对。如果调用者准备好处理它,调用者可以捕获它。但只捕获你准备处理的内容。
catch(ArgumentException ex)
很好,
catch(Exception ex)
或just
catch
是不可取的。@soo:如果您希望您的呼叫者尝试抛出太多shuriken,那么我认为,除了抛出
ArgumentException
,您还应该有一个
bool TryThrowShuriken(int n)
方法,如果
n
太大,该方法将返回false。但是,将
ArgumentException
保留在
ThrowShuriken
中,以防作弊。关于什么是异常,什么不是异常,你提出了一个很好的观点。我认为在你的代码中,你仍然有异常的可能性,我不可能抛出-4项,我不知道“我不想
public class Ninja{
        Ninja(){
        }

        public void ThrowShirikin(int numberOfShirikins){
                if(numberOfShirikins == 0){ throw NumberOfShirikinsException(); }
                else{ /* do something here */ }
        }