【问题标题】:Resharper Exception rethrow possibly intendedResharper 异常重新抛出可能是有意的
【发布时间】:2009-11-21 20:03:37
【问题描述】:

考虑这种方法(请原谅 Chuck Norris 幽默的可悲尝试:)):

public class ChuckNorrisException : Exception
{
    public ChuckNorrisException()
    {
    }

    public ChuckNorrisException(string message)
        : base(message)
    {
    }

    public ChuckNorrisException(string message, Exception cause)
        : base(message, cause)
    {
    }

    protected ChuckNorrisException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }
}

static void ExceptionTest(double x)
{
    try
    {
        double y = 10 / x;
        Console.WriteLine("quotient = " + y);
    }
    catch (Exception e)
    {
        e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) :
            e;
        throw e;
    }
}

在 resharper 中,我在“throw e”行收到一条警告,上面写着“可能有意重新抛出异常”。但显然在这种情况下这不是本意,因为 e 可以被包裹在 ChuckNorrisException 中,如果我只使用“throw”,则不会抛出包裹的异常。

我知道我可以抑制重新锐化警告,但如果我没记错的话,它将在所有场景中被关闭。我只是想知道是否有其他人遇到过这种情况。我发现的唯一解决方法是创建另一个异常变量(例如 e2),然后抛出它。这可能是我在这里能做的最好的事情了。似乎 resharper 可以检测到这个问题,并且足够聪明,知道如果 e 被修改,那么 throw e 就可以了。

谢谢。

[编辑] 对不起,我忘了一步。在抛出之前,我需要记录异常,所以我不能这样做:

e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) :
            e;
throw e;

我必须做的:

e = e is DivideByZeroException ? new ChuckNorrisException("Only Chuck Norris can divide by 0!", e) :
            e;
LogException(e);
throw e;

【问题讨论】:

  • 为什么必须在抛出异常之前记录它(即使包装在另一个异常中)?为什么调用者不能记录异常,包括内部异常?
  • 例如,考虑这是否是 REST 服务中的服务方法,并且您希望在服务器日志文件中记录该异常。因此调用者(例如:Web 客户端)可以记录异常,但最好能够访问服务器日志本身的异常信息。
  • 客户端无法捕获服务器上 .NET 代码中引发的异常。异常必须在服务器的某个地方被捕获,否则服务器会崩溃。
  • @Adam Ralph -“客户端无法捕获服务器上 .NET 代码中引发的异常”这不完全正确。例如,您可以将 FaultException 与 WCF 服务一起使用,并且可以在客户端上处理该 FaultException。在我们的例子中,我们在服务器上记录该异常,并在客户端捕获 FaultException 并将其也记录在那里。您可以参考这里了解更多信息:msdn.microsoft.com/en-us/library/cc949036.aspx

标签: c# exception resharper


【解决方案1】:

也许我不明白这个问题,所以如果我的方法有误,请纠正我。

这里有两种情况:

  1. 首先是你抓住 原来的例外。然后你包装 它在一个新的异常实例中为 内部异常,然后抛出 新的一个。在这种情况下不会丢失任何信息(内部异常会保留所有信息),因此不会给出警告。

  2. 第二个是你抓住并 重新抛出原来的异常。如果你重新投掷,你 永远不要使用“throw e”,因为它 将篡改堆栈跟踪。 这就是 ReSharper 打印一个 警告。要重新抛出捕获的异常,您应该单独使用“throw”关键字。

this question 的答案比我能解释得更好。由于微妙的副作用和大量错误的人,我个人认为 re-throw 语法有缺陷。

无论如何,这是对您收到警告的原因的描述。这就是我要做的:

catch(DivideByZeroException e)
{
    // we don't catch any other exceptions because we weren't
    // logging them or doing anything with the exception before re-throwing
    throw new ChuckNorrisException("Useful information", e);
}

*Edit -- 如果你需要记录异常,你可以这样做。注意:这是我的首选解决方案,因为我认为与自己查询异常类型相比,它读起来更好,并且包含错​​误的可能性更小:

// catch most specific exception type first
catch(DivideByZeroException e)
{
    Log(e); 
    throw new ChuckNorrisException("Useful information", e);
} 
catch(Exception e) // log & re-throw all other exceptions
{
    Log(e);
    throw; // notice this is *not* "throw e"; this preserves the stack trace
}

另一种选择是:

catch(Exception e)
{
    Log(e);
    if(e is DivideByZeroException)
    {
        // wrap + throw the exception with our new exception type
        throw new ChuckNorrisException("useful info", e);
    }

    // re-throw the original, preserving the stack trace
    throw;
}

【讨论】:

  • 很好的答案!我不知道“throw e”会弄乱堆栈跟踪。顺便说一句,如果其他人对此感兴趣,如果您想了解更多信息,请提供有关此主题的一个很好的链接:tkachenko.com/blog/archives/000352.html 我也不知道的一件事是,这种行为在 C# 与 JAVA 中是不同的。再次感谢马克!
【解决方案2】:

这将与您发布的代码具有相同的效果,并且不应导致警告。

catch (DivideByZeroException de)
    {
        throw new ChuckNorrisException("Only Chuck Norris can divide by 0!", de);
    }
}

【讨论】:

  • 你是对的,但是,我在最初的问题中遗漏了一步(我的错)。很抱歉。
猜你喜欢
  • 1970-01-01
  • 2012-10-24
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多