检测内部异常的最佳方法是什么?

68

有时候innerException是空的。

所以以下代码可能会失败:

 repEvent.InnerException = ex.InnerException.Message; 

有没有一种快速的三元方式来检查 innerException 是否为 null?


1
您可能需要重新考虑您接受的答案。jrista的答案比其他答案更好,因为InnerException可以有自己的InnerException。 - Ryan Lundy
4
请记住,ToString会遍历内部异常并将它们组合起来,这可以是记录日志时的便捷快捷方式。 - Steven Sudit
16个回答

87

到目前为止,已经有很棒的答案了。不过,有时候可能会有多层嵌套的异常。如果想要获取最初引发的根异常,无论有多深,可以尝试这样做:

public static class ExceptionExtensions
{
    public static Exception GetOriginalException(this Exception ex)
    {
        if (ex.InnerException == null) return ex;

        return ex.InnerException.GetOriginalException();
    }
}

并且在使用中:

repEvent.InnerException = ex.GetOriginalException();

我是否遗漏了什么,还是ex.GetOriginalException()用于获取内部异常而不是顶层异常? - JL.
谢谢,这帮助我找到了EntityFramework抛出的异常的InnerException。 - Blake Mumford
我不确定哪个版本的.Net Framework提供了GetOriginalException,但我所看到的只有GetBaseException()。它们是一样的吗? - Antony
3
他的意思是你应该实现自己的方法,名为 GetOriginalException。但是,是的,似乎 GetBaseException() 做了同样的事情。 - Justin T Conroy
2
我刚刚有一个例子,尝试对 HttpClient 的 Task<String> 使用 GetBaseException,但它继续返回更高级别的异常。当我实现这个解决方案时,它返回了我需要的“机器主动拒绝连接”的消息。干得好,Jrista! - Dan Chase
显示剩余2条评论

67

这是你在寻找的东西吗?

String innerMessage = (ex.InnerException != null) 
                      ? ex.InnerException.Message
                      : "";

3
是我自己的感觉还是把参数换一下位置,然后把 != 改成 == 会更加简洁易懂? - Noldorin
1
@Noldorin - 抱歉,伙计,我回答了这个问题,但没有检查我是否第一个回答! @JL - Noldorin是第一位给出正确答案的,请把采纳答案转移给他。 - Andrew Hare
@Andrew:嘿,不用道歉。:) 我并不是那种会因为一个微不足道的问题而生气的人,我只是对JL的原因感到好奇而已。 - Noldorin
1
可以工作,但不能处理多层嵌套的异常。 - krystan honour

50

太好了!有了这个,您就不必编写自定义方法来遍历所有嵌套的异常了。 - armadillo.mx
1
我刚刚遇到了一个例子,我试图对 HttpClient 的 Task<String> 使用 GetBaseException,但它仍然只返回一个更高级别的异常。当我实施 Jrista 的解决方案时,它返回了我正在寻找的“机器主动拒绝…”消息。 - Dan Chase
为什么这个答案还没有被接受为正确答案? - Thameem
@Thameem,似乎有些情况下这种方法不起作用(例如,Dan Chase上面所说的)。我遇到了类似的问题。 - pushkin

17
最简单的解决方案是使用基本条件表达式:
repEvent.InnerException = ex.InnerException == null ? 
    null : ex.InnerException.Message;

完全没有问题,只是安德鲁的代码返回了一个字符串。 - JL.
@JL: 实际上,mind 也返回一个 string。我只是根据代码假定你调用了 InnerException 属性(只是为了混淆吗?)。不过没关系。 @Andrew: 谢谢。虽然我完全不介意,但 +1 给你的礼貌。 - Noldorin
@Nordorin,啊,我明白问题出在哪里了,我使用字符串是因为异常无法很好地序列化(如果它们真的可以)。 - JL.
@JL:随着.NET框架一起提供的异常可以很好地序列化,因为它们都有反序列化构造函数。自定义异常可能无法正确序列化,但很容易添加反序列化构造函数并覆盖GetObjectData方法。第三方扩展可以通过序列化代理使其可序列化。 - jrista

15

为什么这些答案中有那么多递归?

public static class ExceptionExtensions
{
    public static Exception GetOriginalException(this Exception ex)
    {
        while(ex.InnerException != null)ex = ex.InnerException;
        return ex;
    }
}

看起来这是一种更加直观的实现方式。


12

这是一个旧问题,但对于未来的读者:

除了已经发布的答案,我认为正确的做法(当您可以有多个InnerException时)是Exception.GetBaseException方法

如果您想要异常实例,应该这样做:

repEvent.InnerException = ex.GetBaseException();

如果您只是想通过这种方式查找消息:

repEvent.InnerException = ex.GetBaseException().Message;

9

使用C# 6.0,您可以使用以下代码:

string message = exception.InnerException?.Message ?? "";

这行代码与以下代码类似:

string message = exception.InnerException == null ? "" : exception.InnerException.Message.

https://msdn.microsoft.com/en-us/library/ty67wk28.aspx

http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx

注:上述代码使用了安全导航运算符,如果exception.InnerException为null,则返回空字符串。

+1 空值条件运算符和空值合并运算符。使用(ex.InnerException?.Message??"")方便、可读且安全。 - dakab

6
使用这段代码,你可以确保没有丢失任何内部异常消息。
catch (Exception exception)
{
   Logger.Error(exception.Message);
   while (exception.InnerException != null)
   {
       exception = exception.InnerException;
       Logger.Error(exception);
   }
}

我经常使用 while (ex.InnerException != null) 方法,发现它非常有用。然而,有时它可能变得非常冗长。 - Zimano

6
使用C# 6.0,您可以在一行代码中完成此操作。
repEvent.InnerException = ex.InnerException?.Message; 

点击这里查看C# 6.0的其他功能。


以上代码的最大优点是,您无需担心空指针异常,即在使用之前(显式地)检查空值。 - Ashwin

5
有时候 InnerException 也有自己的 InnerException,因此您可以使用递归函数处理它:
public string GetInnerException(Exception ex)
{
     if (ex.InnerException != null)
     {
        return string.Format("{0} > {1} ", ex.InnerException.Message, GetInnerException(ex.InnerException));
     }
   return string.Empty;
}

1
我非常赞成深入挖掘最古老的异常,但在这里真的没有必要使用递归。只需在内部异常不为null时进行循环即可。 - Steven Sudit
递归是邪恶的还是什么? - tster
在Entity Framework中,有一些有用的异常深藏其中。 - Jan Remunda
递归并不是邪恶的,但它可能会很耗费资源,所以如果你不需要它,就不要使用它。在内部异常的情况下,你没有必要将当前状态推入堆栈中,以便可以从上次离开的地方继续执行,那么为什么要使用递归并付出这个代价呢?此外,有些情况下递归比常规迭代慢得多,比如计算斐波那契数列。 - Steven Sudit
太棒了!我把它添加为扩展方法以便在整个项目中使用。 - Korayem
@StevenSudit,递归在这里不可能是昂贵的。他绝对不可能炸栈。递归和迭代版本之间的差异是无法检测到的。唯一值得担心的是哪个更容易理解。 - dan-gph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接