C# 重构异常处理

C# 重构异常处理,c#,exception,refactoring,C#,Exception,Refactoring,在我的一个类中,我调用了一个存储库,其中包含一些错误处理。我想重构错误处理代码,因为它是非常重复的,唯一真正改变的是消息 我的代码当前看起来像这样: public IList<User> GetUser() { try { return _repository.GetUsers(); } catch (WebException ex) { ErrorMessages.Add("..."); _lo

在我的一个类中,我调用了一个存储库,其中包含一些错误处理。我想重构错误处理代码,因为它是非常重复的,唯一真正改变的是消息

我的代码当前看起来像这样:

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
}
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;
}
public IList GetUser()
{
尝试
{
返回_repository.GetUsers();
}
捕获(WebException ex)
{
ErrorMessages。添加(“…”);
_logger.ErrorException(“…”,ex);
}
捕获(SoapException-ex)
{
ErrorMessages。添加(“…”);
_logger.ErrorException(“…”,ex);
}
等
}
我可以用调用另一个方法来替换catch块中的那些行,该方法接受错误消息值和记录器消息值。然而,我想我也可以使用Action参数来实现这一点,但我在使用Func和Action方面非常缺乏经验,并且不知道使用其中一个参数比使用一个方法有什么好处

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


感谢您的帮助。

假设异常类型始终相同,但消息不同,您可以执行以下操作:

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
,而是使用
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());
IList用户;
试试(“我的web消息。”,“我的soap消息。”,()=>users=\u repository.GetUsers());

您可以使用lambdas来帮助实现这一点

如果将通用错误处理程序定义为接受
Action
类型的参数,则可以在错误处理程序中调用该操作

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

例如,您的常规处理程序可能如下所示:

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
}
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;
}
然后你可以这样使用它:

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
}
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;
}
public IList GetUser()
{
IList result=null;
AttemptAction(()=>result=_repository.GetUsers());
返回结果;
}

您可以使用面向方面的编程。 将所有重复代码放在称为方面的特殊类中的想法

您的代码将与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);
    }
}
[例外日志]
公共IList GetUser()
{
返回_repository.GetUsers();
}
公共类ExceptionLogger:OnMethodBoundaryAspect
{
//获取记录器和错误消息
public override void OnException(MethodExecutionArgs args)
{
ErrorMessages。添加(“…”);
_logger.ErrorException(“…”,ex);
}
}

对于c#,您可以使用PostSharp、Castle.Windsor或Unity框架。

请注意,
T的返回类型将使处理无效方法变得有点笨拙。另一个例子是,您不应该仅仅因为可以这样做就这样做。这个问题的答案很好。@gbjbaanb:谢谢!但是为什么不应该这样做?我承认这不是我最喜欢的构造,我可以用它编写很多代码。我也很想知道为什么你不应该这么做。顺便说一下谢谢你的回答为什么?!“在离用户最近的层中,尝试/捕获所有内容都是正常的。”。最终,此结果将返回到视图。感谢您的回答。但是,我认为应该是相反的,因为错误消息对于所有调用都是不同的。有一个GetUsers调用,但也有其他调用,如GetCategories。我应该在我原来的帖子中提到这一点,sorry@Serberuss没关系,这是一种通用方法。如果需要,您可以像MartinMulder的回答一样传递错误消息。