重构异常处理

本文关键字:异常处理 重构 | 更新日期: 2023-09-27 18:14:40

在我的一个类中,我有一个对存储库的调用,它有一些错误处理。我想重构错误处理代码,因为它是相当重复的,唯一真正改变的是消息。

我的代码现在看起来像这样:

public IList<User> GetUser()
{
    try
    {
        return _repository.GetUsers();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
    }
    catch (SoapException ex)
    {
       ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
    }
    ... etc
}

我可以将catch块中的这些行替换为对另一个方法的调用,该方法接受错误消息值和记录器消息值。然而,我想我也可以使用Action<>参数来做到这一点,但我在使用Func<>和Action<>方面非常缺乏经验,并且没有真正看到使用它们中的一个比方法有什么好处。

我的问题是重构这段代码的最佳方式是什么,为什么一种方式比另一种方式更有利(就像我上面的例子一样)。

谢谢你的帮助

重构异常处理

你可以使用lambdas来帮助你。

如果你定义了通用错误处理程序来接受类型为Action的参数,那么你可以在错误处理程序中调用该操作。

您不需要担心返回值,因为在调用时编写的lambda可以处理这个问题。

例如,您的通用处理程序可能像这样:

public void AttemptAction(Action action)
{
    try
    {
        action();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
        // Rethrow?
    }
    catch (SoapException ex)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
        // Rethrow?
    }
}

然后你可以这样使用:

public IList<User> GetUser()
{
    IList<User> result = null;
    AttemptAction(() => result = _repository.GetUsers());
    return result;
}

假设异常类型总是相同的,但消息不同,您可以这样做:

static public T Try<T>(string webMessage, string soapMessage, Func<T> func)
{
    try
    {
        return func();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add(webMessage);
        _logger.ErrorException(webMessage, ex);
    }
    catch (SoapException ex)
    {
       ErrorMessages.Add(soapMessage);
        _logger.ErrorException(soapMessage, ex);
    }
}

这个Try-method将使用Func<T>类型的委托来调用函数并返回其值。函数将位于同一个try-catch块中。消息是通过参数提供的。现在,在代码的其他地方,你可以这样调用它:

var users = Try("My web message.", "My soap message.", () => _repository.GetUsers());

或者,在您的情况下更短(当不使用参数时):

var users = Try("My web message.", "My soap message.", _repository.GetUsers);

当然,您可以根据自己的喜好修改和安排Try的参数。

如果您混合了有返回类型和没有返回类型的方法,最好不要使用Func,而是使用Action。这将能够符合所有情况:

static public void Try(string webMessage, string soapMessage, Action action)
{
    try
    {
        action();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add(webMessage);
        _logger.ErrorException(webMessage, ex);
    }
    catch (SoapException ex)
    {
       ErrorMessages.Add(soapMessage);
        _logger.ErrorException(soapMessage, ex);
    }
}

但是这个解决方案使代码更难阅读/维护:

IList<User> users;
Try("My web message.", "My soap message.", () => users = _repository.GetUsers());

您可以使用面向方面的编程http://en.wikipedia.org/wiki/Aspect-oriented_programming。将所有重复的代码放在称为aspect的特殊类中。

你的代码看起来像PostSharp

[ExceptionLogger]
public IList<User> GetUser()
{
    return _repository.GetUsers();
}
public class ExceptionLogger: OnMethodBoundaryAspect
{
    //getting _logger and ErrorMessages
    public override void OnException(MethodExecutionArgs args)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
    }
}

对于c#,你可以使用PostSharp, Castle。