【问题标题】:How to avoid exceptions catches copy-paste in .NET如何避免异常捕获 .NET 中的复制粘贴
【发布时间】:2011-03-05 02:46:09
【问题描述】:

使用 .NET 框架我有一个带有一组方法的服务,这些方法可以生成多种类型的异常:MyException2、MyExc1、Exception... 为了为所有方法提供正确的工作,每个方法都包含以下部分:

[WebMethod]
void Method1(...)
{
    try
    {
        ... required functionality
    }
    catch(MyException2 exc)
    {
        ... process exception of MyException2 type
    }
    catch(MyExc1 exc)
    {
        ... process exception of MyExc1 type
    }
    catch(Exception exc)
    {
        ... process exception of Exception type
    }
    ... process and return result if necessary
}

在每个服务方法中都有完全相同的东西(每个方法有不同的参数集)和完全相同的异常处理功能是非常无聊的......

是否有可能将这些捕获部分“分组”并仅使用一行(类似于 C++ 宏)?可能 .NET 4.0 中的一些新内容与此主题相关?

谢谢。

附:欢迎任何想法。

【问题讨论】:

    标签: .net asp.net exception copy-paste


    【解决方案1】:

    如果您的所有方法中的异常处理完全相同,您可以执行以下操作:

    void CallService(Action method)
    {
        try
        {
            // Execute method
            method();
        }
        catch(MyException2 exc)
        {
            ... process exception of MyException2 type
        }
        catch(MyExc1 exc)
        {   
            ... process exception of MyExc1 type
        }
        catch(Exception exc)
        {
            ... process exception of Exception type
        }
    }
    

    然后,你可以重写你的客户端代码来做:

    int i = 3;
    string arg = "Foo";
    this.CallService( () => this.Method1(i) );
    this.CallService( () => this.Method2(arg, 5) );
    

    这使您的 Method1 和 Method2 方法变得简单:

    void Method1(int arg)
    {
        // Leave out exception handling here...
        ... required functionality  
        ... process and return result if necessary
    }
    
    void Method2(string stringArg, int intArg)
    {
        // Leave out exception handling here...
        ... required functionality  
        ... process and return result if necessary
    }
    

    【讨论】:

    • 我也有类似的想法,但问题是每种方法都有不同的参数集(对不起,我在原始问题中没有提到这一点,最近添加了规范)。这是方法签名的示例: string Method1(string); void Method2(string, int, int) 等...
    • @Budda:没关系 - 它可以正常工作,因为 lambdas 可以传入所有参数,就像我写的那样。
    • @Budda:我编辑了我的答案来演示:) 这适用于方法中的任意数量的参数...
    • 谢谢。但在这种情况下,我们需要为每个“MyMethodX”添加额外的方法,对吧?我会避免这种情况......这可以通过匿名方法来实现,如下所示: this.CallService( () => { ... required features} );没有单独的方法定义......对吗? :) 再次感谢您。
    • @Budda:是的,如果您不需要,您可以完全避免使用该方法。我只是想把你的例子移植过来。你在这里有很大的灵活性......
    【解决方案2】:

    为什么不直接将代码分解为一个帮助方法来为您执行此操作(您也可以将将来需要的任何新异常添加到 HandleException,这使得它具有相当大的可扩展性)?

    try
    {
        ... required functionality
    }
    catch (Exception e)
    {
        HandleException(e);
        throw; // only if you need the exception to propagate to caller
    }
    
    
    private void HandleException(Exception e)
    {
        if (e is MyException2)
        {
            ... process exception of MyException2 type
        }
        else if (e is MyExc1)
        {
            ... process exception of MyExc1 type
        }
        else
        {
            ... process exception of Exception type
        }
    }
    

    【讨论】:

    • dcp,为什么在调用 HandleException(e) 后重新抛出异常?
    • 这种模式违反了微软的代码分析规则“不捕获一般异常类型”msdn.microsoft.com/en-us/library/ms182137(v=VS.90).aspx
    • @chilltemp - 来吧,不要重复你的自我规则具有更高的优先级。
    • @Budda - 重新抛出是为了让调用者可以处理异常。如果不需要,则可以删除重新抛出,但通常您希望让异常传播,以便调用者知道失败了。
    • @chillitemp - 我认为 Microsft 规则背后的想法是你最好处理特定的异常(如果你知道你期望什么异常)并采取适当的行动,而不是仅仅使用 catch(例外 e)。不过,在这种情况下,我们将在 HandleException 方法中执行特定操作。所以我们真的秉承了规则的精神,我们只是做了一些不同的事情来实现代码重用,正如@ChaosPandion 指出的那样,这确实具有更高的优先级。
    【解决方案3】:

    我会仔细研究您为“处理”这些异常所做的工作。很有可能您根本不需要 catch 块,并且您应该允许传播异常。

    【讨论】:

    • 这些方法是 Web 服务的 [WebMethod],它们应该总是返回对客户端有意义的东西。所有异常处理部分都生成“错误”对象,该对象被序列化为 XML 格式并发送到客户端。你的建议给了我一个优化错误对象生成的想法......它可以用工厂模式实现......通过以下方式:catch (Exception exc) { MyError error = MyError.GenerateErrorObject(exc); ...过程(错误); } 谢谢。
    • @Budda: 1) 很高兴知道。下次再说吧。 2) 您应该抛出 SoapException 并设置详细信息以返回 SOAP 错误。
    • 我需要以某些自定义格式发送消息,并且 SOAP 异常不适合 :)
    【解决方案4】:

    突然出现极客(并展示您可以但很可能不应该和不想做的事情):您可以通过动态生成包含异常处理的函数来使整个事情更具组合性和可重用性逻辑:

    static class ExceptionHandlerExtensionMethods 
    {
        // extend to Func<T> as desired
        public static Action Catching<T>(this Action what, Action<T> handler) 
            where T : Exception
        {
            return () =>
            {
                 try
                 {
                     what();
                 }
                 catch (T ex)
                 {
                     handler(ex);
                 }
             };
        }
    }
    

    现在您可以在某处实现和重用特定于异常类型的处理函数,并将它们组合到其他方法的异常处理中。

    要消除冗余,您可以编写一个辅助函数,将“典型”异常处理函数添加到您想要调用的任何内容中,然后调用此修饰方法。

    【讨论】:

      【解决方案5】:

      使用 Reed Copsey 的 CallService 方法:

      void DoSomething(object param1, int param2)
      {
          this.CallService(() =>
          {
               // work with param1 and param2 here
          }
      }
      

      对于需要返回值的情况,可能需要复制 CallService 来返回类型参数。

      T CallService<T>(Func<T> callback) { /* ... */ }
      

      【讨论】:

        【解决方案6】:

        我知道这是不好的做法,但是如果您在每个 catch 语句中都有完全相同的错误处理,那么为什么不直接使用最后一个 catch 语句作为全部捕获呢?

        这当然假设您的所有异常都继承自 Exception。

        【讨论】:

        • 我不是——我只是想知道为什么你会在多个 catch 语句中处理完全相同的错误,因为拥有多个 catch 的关键是你处理你的异常的方式不同.. .
        • 我有完全相同的处理,以便为所有方法提供相同的错误处理。
        • 我认为您误解了这个问题。如果我理解正确:在他的一种方法中,每个 catch{} 块都没有相同的处理。他拥有的是同一组 catch{} 块和跨多个方法的处理
        • 你说得对,我没有抓住原始问题中关于多种方法的部分。道歉。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-07
        • 1970-01-01
        • 2023-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多