【问题标题】:Refactoring exception handling重构异常处理
【发布时间】:2013-05-20 11:24: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 方面非常缺乏经验,并且真的不知道使用其中一个参数比使用方法有什么好处。

我的问题实际上是重构此代码的最佳方式是什么,以及为什么一种方式比另一种方式受益(根据我上面的示例)。

感谢您的帮助。

【问题讨论】:

标签: c# exception refactoring


【解决方案1】:

您可以使用 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;
}

【讨论】:

  • 感谢您的回答。但是,我认为它需要反过来,因为所有调用的错误消息都不相同。有一个 GetUsers 调用,但也有其他调用,例如 GetCategories。我应该在我原来的帖子中提到这一点,对不起
  • @Serberuss 没关系 - 这是一种通用方法。如果需要,您可以像 MartinMulder 的回答一样传递错误消息。
【解决方案2】:

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

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 方法将使用 Func&lt;T&gt; 类型的委托来调用函数并返回其值。该函数将在同一个 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());

【讨论】:

  • 请注意,T 的返回类型会使处理 void 方法有点尴尬。
  • 另一个你不应该仅仅因为你能做到的事情的例子。不过这个问题的答案很好。
  • @gbjbaanb:谢谢!但是..为什么不应该这样做?我承认这不是我最喜欢的结构,我可以写很多代码。
  • 我也很想知道您为什么不应该这样做。顺便谢谢你的回答
【解决方案3】:

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

您的代码将类似于 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.Windsor 或 Unity 框架。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    • 2011-10-10
    • 1970-01-01
    • 2014-01-06
    相关资源
    最近更新 更多