在 C# 中制作可重用的 try/catch 块的建议?

问题描述 投票:0回答:5

我有一个类,其中大约有 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 块?还有其他想法吗?

c# .net exception try-catch
5个回答
58
投票

我会这样做:

创建一个包含 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
,如果您认为这样更具可读性,您可以内联它。


8
投票

有很多方法可以轻松做到这一点 - 首先对我来说,我已经开始使用 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 - 详细信息在这里

或者,Mark Rendle 有一篇关于 如何以函数式编程方式捕获异常 的博客文章 - 不过我还没有尝试过这个


5
投票

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

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


1
投票

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


0
投票

您可以将上述代码编写在一个方法中,该方法采用 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);
        }
}
© www.soinside.com 2019 - 2024. All rights reserved.