【问题标题】:Suggestions for making a reusable try/catch block in C#?在 C# 中创建可重用的 try/catch 块的建议?
【发布时间】:2011-08-28 03:09:16
【问题描述】:

我有一个类,其中包含大约 20 种方法。每个都进行一些 Web 服务消息处理。我只需要对其进行更改,并意识到这些方法中的每一种都有完全相同的 try/catch:

        try
        {
            /* *** actual processing specific to each method goes here *** */
        }
        catch (FaultException<CustomException> cfex)
        {
            // common stuff
        }
        catch (CustomException cfex)
        {
            // common stuff
        }
        catch (Exception ex)
        {
            // common stuff
        }
        finally
        {
            FinalizeServiceCall(wsBus, wsMessage, response, logProps);
        }

我的问题是;而不是在每个方法中都有这个完全相同的 try/catch 块,有没有办法让它变得普遍?我的想法是.NET 有类似TransactionScope 的东西,它以某种方式检测离开该块时是否发生异常。有没有我可以利用类似的东西来制作一个常见的 try/catch 块?还有其他想法吗?

【问题讨论】:

  • TransactionScope 可能看起来,在其Dispose() 方法中,如果Complete() 被调用(我的猜测是Complete() 将标志设置为true,并且Dipose() 要么提交如果是true,否则回滚)。
  • 为什么不将 try/catch 逻辑上移一个级别?
  • @Cody - 我想过将其提升一个级别,但这些方法中的每一个都是 WCF [OperationContract] 服务方法,因此实际上没有更高级别,除非我创建一个。
  • @Etienne - 你是对的,它检查 Dispose 中的 Complete 方法。我知道,但是当我问这个问题时完全忘记了:)
  • 为什么您的 WCF 实现会捕获错误异常?

标签: c# .net exception-handling try-catch


【解决方案1】:

我会这样做:

创建一个包含 try/catch 的方法,并将 Action 传递给它,并在 try 部分中执行该操作:

public void Method1()
{
    Action action = () =>
    {
        // actual processing of Method 1
    };
    SafeExecutor(action);
}

public void Method1b()
{
    SafeExecutor(() =>
    {
        // actual processing of Method 1
    });
}

public void Method2(int someParameter)
{
    Action action = () =>
    {
        // actual processing of Method 2 with supplied parameter
        if(someParameter == 1)
        ...
    };
    SafeExecutor(action);
}

public int Method3(int someParameter)
{
    Func<int> action = () =>
    {
        // actual processing of Method 3 with supplied parameter
        if(someParameter == 1)
            return 10;
        return 0;
    };
    return SafeExecutor(action);
}

private void SafeExecutor(Action action)
{
    SafeExecutor(() => { action(); return 0; });
}

private T SafeExecutor<T>(Func<T> action)
{
    try
    {
        return action();
    }
    catch (FaultException<CustomException> cfex)
    {
        // common stuff
    }
    catch (CustomException cfex)
    {
        // common stuff
    }
    catch (Exception ex)
    {
        // common stuff
    }
    finally
    {
        FinalizeServiceCall(wsBus, wsMessage, response, logProps);
    }

    return default(T);
}

SafeExecutor 的两个版本让您可以处理带有和不带有返回类型的方法。
Method1b 表明您的方法中不需要变量action,您可以内联它,如果您认为这更具可读性。

【讨论】:

  • 这正是我所创造的。好东西。
  • 略显冗长:return SafeExecutor(() =&gt; { return 42 });
  • Action 无法返回值。如果要返回值,需要使用Func&lt;T&gt;
  • 正如 Cody Gray 评论的那样,将 try/catch 提升一个级别可能会更简单。通过在每种方法中捕获异常,您可以获得什么?
  • 这绝对符合我的想法。我打算基本上实现同样的事情,但我想把它贴在 SO 上,看看是否有一种通常更好或更被接受的方式来完成它。感谢您的回答!
【解决方案2】:

有一些方法可以让你轻松做到这一点——首先我已经开始使用 AOP 来捕获我的异常

这将有效地改变你的代码

try
        {
            /* *** actual processing specific to each method goes here *** */
        }
        catch (FaultException<CustomException> cfex)
        {
            // common stuff
        }
        catch (CustomException cfex)
        {
            // common stuff
        }
        catch (Exception ex)
        {
            // common stuff
        }
        finally
        {
            FinalizeServiceCall(wsBus, wsMessage, response, logProps);
        }

变成类似的东西

[HandleException( Exception , FaultException<CustomException>, 
                      "Error Getting Details" )]
    public MYType GetDetails( string parameter )
    {
        //.... call to service
    }

使用 Postsharp - details here

另外,Mark Rendlehow to catch exceptions in a Functional Programming way 上也有一篇博文——不过我还没有尝试过这个

【讨论】:

  • 我绝对喜欢 AOP 方法,这可能是我在之前处理 AOP 的 Java 项目中所做的。在我这个项目的特殊情况下,我没有使用 Postsharp,但仍然 +1 以获得非常好的答案。
  • PostSharp 有什么好的替代品,我可以创建一个类似的“HandleException”属性来捕获我的异常?我查看了一些替代方案,但似乎其他框架比它们的价值更麻烦。
【解决方案3】:

您已经确定了一个跨领域关注。您可以采用面向方面的编程 (AOP) 方法来解决此问题。这可以在运行时通过使用位于类前面的代理来执行,也可以在编译期间通过使用修改已编译代码的 AOP 工具来执行。

过去我曾使用Castle Dynamic Proxy 来执行此操作(在运行时)。或者,您可以使用其他 AOP 框架之一,例如 PostSharp

【讨论】:

  • 您有任何使用 Castle Dynamic Proxy 完成此任务的示例吗?
【解决方案4】:

如果参数相同或接近相同,您始终可以传入委托。如果它们不是您可以通过反射调用代码并采用“object []”的参数传递给调用。

【讨论】:

    【解决方案5】:

    您可以做的是在一个方法中编写上面的代码,该方法接受一个 Action 或一个 Func 作为参数,该参数确定应该在 throw 块中调用的方法及其参数。

    因此,如果您在 throw 块中调用 M(1, "string"),它将变为 DoStuff(M, 1, "string")

    DoStuff 看起来像

    void DoStuff<T1, T2, TResult>(Func<T1, T2, TResult> myMethod, T1 arg1, T2 arg2)
    {
            try
            {
                myMethod(arg1, arg2)
            }
            catch (FaultException<CustomException> cfex)
            {
                // common stuff
            }
            catch (CustomException cfex)
            {
                // common stuff
            }
            catch (Exception ex)
            {
                // common stuff
            }
            finally
            {
                FinalizeServiceCall(wsBus, wsMessage, response, logProps);
            }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 2010-10-31
      • 2011-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多