【问题标题】:Find the inner-most exception without using a while loop?在不使用while循环的情况下找到最里面的异常?
【发布时间】:2011-04-22 01:05:33
【问题描述】:

当 C# 抛出异常时,它可以有一个内部异常。我想要做的是获得最内部的异常,或者换句话说,没有内部异常的叶异常。我可以在 while 循环中执行此操作:

while (e.InnerException != null)
{
    e = e.InnerException;
}

但我想知道是否有一些单行线可以用来代替。

【问题讨论】:

  • 我在我的一个类中抛出了一个异常,但是我的类被一个吞下所有异常并抛出自己的异常的库使用。问题是,库异常非常通用,我需要知道我抛出了哪个特定异常才能知道如何处理问题。更糟糕的是,这个库会抛出自己的异常多次嵌套,达到任意深度。例如,它将抛出LibraryException -> LibraryException -> LibraryException -> MyException。我的异常始终是链上的最后一个异常,并且没有自己的内部异常。
  • 哦,我明白了为什么你可能会降到最里面,我自己已经这样做了(以及变体,例如从特定类型派生的最里面),但我不明白问题出在哪里你已经拥有的。
  • 哦,我明白你的意思了。我只是想知道是否有另一种写法。我已经习惯使用LINQ 来做几乎所有事情,以至于当我不得不编写一个循环时,这似乎很奇怪。我主要从事数据访问工作,所以我几乎所有工作都使用ICollections
  • @Daniel,LINQ 不只是掩盖了代码仍然必须循环的事实吗? .NET 中有真正的基于集合的命令吗?
  • 最正确的答案潜伏在底部附近,(目前)只有 2 票——Exception.GetBaseException()。这基本上永远存在于框架中。感谢 batCattle 的完整性检查。

标签: c# .net exception exception-handling while-loop


【解决方案1】:

Oneliner :)

while (e.InnerException != null) e = e.InnerException;

显然,你不能让它变得更简单。

正如 Glenn McElhoe 在this answer 中所说,这是唯一可靠的方法。

【讨论】:

  • GetBaseException() 方法在没有循环的情况下执行此操作。 msdn.microsoft.com/en-us/library/…
  • 这有效,而 Exception.GetBaseException() 不适合我。
  • 此代码是否会更改“e”源异常?我们不应该在将 InnerException 分配给它之前复制“e”吗?我建议: var copyOfException = e; while (copyOfException.InnerException != null) copyOfException = copyOfException.InnerException;
【解决方案2】:

我相信Exception.GetBaseException() 与这些解决方案做的事情相同。

警告:从各种 cmets 中,我们发现它并不总是字面上做同样的事情,在某些情况下,递归/迭代解决方案会让你走得更远。 通常是最里面的异常,令人失望的不一致,这要归功于某些类型的异常覆盖了默认值。但是,如果您捕获特定类型的异常并合理地确保它们不是古怪的(例如 AggregateException),那么我希望它会获得合法的最内层/最早的异常。

【讨论】:

  • +1 没有什么比了解 BCL 更能避免复杂的解决方案了。显然,这是最正确的答案。
  • 由于某种原因,GetBaseException() 没有返回第一个根异常。
  • Glenn McElhoe 指出,确实 GetBaseException() 并不总是像 MSDN 建议的那样我们通常可以预期的那样(即“异常是一个或多个后续异常的根本原因”)。在 AggregateException 中它仅限于同一类型的最低异常,并且可能还有其他异常。
  • 不适用于我的问题。我比它提供的还低了几个级别。
  • GetBaseException() 对我不起作用,因为最上面的异常是 AggregateException。
【解决方案3】:

循环 InnerExceptions 是唯一可靠的方法。

如果捕获的异常是 AggregateException,则 GetBaseException() 仅返回最里面的 AggregateException。

http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx

【讨论】:

  • 好收获。谢谢 - 这解释了上面的 cmets,其中的答案对某些人不起作用。这清楚地表明,MSDN 对“一个或多个后续异常的根本原因”的一般描述并不总是您所假设的,因为派生类可能会覆盖它。真正的规则似乎是异常链中的所有异常都“同意”GetBaseException() 并返回相同的对象,这似乎是 AggregateException 的情况。
【解决方案4】:

如果您不知道内部异常嵌套的深度,则无法绕过循环或递归。

当然,您可以定义一个扩展方法来抽象它:

public static class ExceptionExtensions
{
    public static Exception GetInnermostException(this Exception e)
    {
        if (e == null)
        {
            throw new ArgumentNullException("e");
        }

        while (e.InnerException != null)
        {
            e = e.InnerException;
        }

        return e;
    }
}

【讨论】:

    【解决方案5】:

    我知道这是一篇旧帖子,但我很惊讶没有人建议 GetBaseException() 这是 Exception 类的方法:

    catch (Exception x)
    {
        var baseException = x.GetBaseException();
    }
    

    这从 .NET 1.1 开始就存在了。此处的文档: http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception(v=vs.71).aspx

    【讨论】:

    • 确实是在 2012 年 12 月 5 日之前指出的
    【解决方案6】:

    有时您可能有许多内部异常(许多冒泡异常)。 在这种情况下,您可能想要这样做:

    List<Exception> es = new List<Exception>();
    while(e.InnerException != null)
    {
       es.add(e.InnerException);
       e = e.InnerException
    }
    

    【讨论】:

      【解决方案7】:

      您可以使用递归在某处的实用程序类中创建方法。

      public Exception GetFirstException(Exception ex)
      {
          if(ex.InnerException == null) { return ex; } // end case
          else { return GetFirstException(ex.InnerException); } // recurse
      }
      

      用途:

      try
      {
          // some code here
      }
      catch (Exception ex)
      {
          Exception baseException = GetFirstException(ex);
      }
      

      建议的扩展方法(好主意@dtb)

      public static Exception GetFirstException(this Exception ex)
      {
          if(ex.InnerException == null) { return ex; } // end case
          else { return GetFirstException(ex.InnerException); } // recurse
      }
      

      用途:

      try
      {
          // some code here
      }
      catch (Exception ex)
      {
          Exception baseException = ex.GetFirstException();
      }
      

      【讨论】:

      • 有用 public static Exception GetOriginalException(this Exception ex) { if (ex.InnerException == null) return ex; return ex.InnerException.GetOriginalException(); }
      • 不适用AggregateException.InnerExceptions ?
      【解决方案8】:

      不完全是一行,但很接近:

              Func<Exception, Exception> last = null;
              last = e => e.InnerException == null ? e : last(e.InnerException);
      

      【讨论】:

        【解决方案9】:

        其实就是这么简单,你可以用Exception.GetBaseException()

        Try
              //Your code
        Catch ex As Exception
              MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
        End Try
        

        【讨论】:

        • 感谢您的解决方案!很少有人在 VB 中思考:D
        • 这是有原因的! :P
        【解决方案10】:

        你必须循环,而且必须循环,将循环移动到一个单独的函数中会更干净。

        我创建了一个扩展方法来处理这个问题。它返回指定类型的所有内部异常的列表,跟踪 Exception.InnerException 和 AggregateException.InnerExceptions。

        在我的特定问题中,追踪内部异常比平常更复杂,因为异常是由通过反射调用的类的构造函数抛出的。我们捕获的异常有一个 TargetInvocationException 类型的 InnerException,而我们真正需要查看的异常被深埋在树中。

        public static class ExceptionExtensions
        {
            public static IEnumerable<T> innerExceptions<T>(this Exception ex)
                where T : Exception
            {
                var rVal = new List<T>();
        
                Action<Exception> lambda = null;
                lambda = (x) =>
                {
                    var xt = x as T;
                    if (xt != null)
                        rVal.Add(xt);
        
                    if (x.InnerException != null)
                        lambda(x.InnerException);
        
                    var ax = x as AggregateException;
                    if (ax != null)
                    {
                        foreach (var aix in ax.InnerExceptions)
                            lambda(aix);
                    }
                };
        
                lambda(ex);
        
                return rVal;
            }
        }
        

        使用非常简单。例如,如果您想知道我们是否遇到了

        catch (Exception ex)
        {
            var myExes = ex.innerExceptions<MyException>();
            if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error")))
            {
                // ...
            }
        }
        

        【讨论】:

        • Exception.GetBaseException() 怎么样?
        【解决方案11】:

        另一种方法是调用GetBaseException() 两次:

        Exception innermostException = e.GetBaseException().GetBaseException();
        

        这是有效的,因为如果它是一个AggregateException,第一次调用会让你到达最里面的非AggregateException,然后第二次调用会让你到达那个异常的最里面的异常。如果第一个异常不是AggregateException,那么第二个调用只会返回相同的异常。

        【讨论】:

          【解决方案12】:

          我遇到了这个问题,希望能够列出异常“堆栈”中的所有异常消息。所以,我想出了这个。

          public static string GetExceptionMessages(Exception ex)
          {
              if (ex.InnerException is null)
                  return ex.Message;
              else return $"{ex.Message}\n{GetExceptionMessages(ex.InnerException)}";
          }
          

          【讨论】:

            猜你喜欢
            • 2019-04-26
            • 1970-01-01
            • 1970-01-01
            • 2012-11-21
            • 2018-07-13
            • 1970-01-01
            • 2013-06-25
            • 1970-01-01
            • 2018-10-05
            相关资源
            最近更新 更多