2012-02-16 126 views
54

有没有什么办法可以写出一个LINQ风格的“短手”代码,用于步行到抛出异常的InnerException(s)的所有级别?我宁愿写它而不是调用一个扩展函数(如下)或继承Exception类。从InnerException(s)获取所有消息?

static class Extensions 
{ 
    public static string GetaAllMessages(this Exception exp) 
    { 
     string message = string.Empty; 
     Exception innerException = exp; 

     do 
     { 
      message = message + (string.IsNullOrEmpty(innerException.Message) ? string.Empty : innerException.Message); 
      innerException = innerException.InnerException; 
     } 
     while (innerException != null); 

     return message; 
    } 
}; 
+2

我可以问你为什么要使用比扩展方法别的东西吗?你的代码对我来说看起来很好,并且可以在代码中的任何地方重用。 – ken2k 2012-02-16 15:45:44

+0

@ ken2k:虽然你不想按照他现在的方式构建消息...... – 2012-02-16 16:11:20

+1

@JeffMercado是的,但是“扩展方法”的概念有什么问题? – ken2k 2012-02-16 16:12:46

回答

63

不幸的是LINQ不提供可以处理层次结构的方法,只能处理集合。

我实际上有一些扩展方法可以帮助做到这一点。我没有在手确切的代码,但他们是这样的:

// all error checking left out for brevity 

// a.k.a., linked list style enumerator 
public static IEnumerable<TSource> FromHierarchy<TSource>(
    this TSource source, 
    Func<TSource, TSource> nextItem, 
    Func<TSource, bool> canContinue) 
{ 
    for (var current = source; canContinue(current); current = nextItem(current)) 
    { 
     yield return current; 
    } 
} 

public static IEnumerable<TSource> FromHierarchy<TSource>(
    this TSource source, 
    Func<TSource, TSource> nextItem) 
    where TSource : class 
{ 
    return FromHierarchy(source, nextItem, s => s != null); 
} 

那么在这种情况下,你可以做到这一点通过例外枚举:

public static string GetaAllMessages(this Exception exception) 
{ 
    var messages = exception.FromHierarchy(ex => ex.InnerException) 
     .Select(ex => ex.Message); 
    return String.Join(Environment.NewLine, messages); 
} 
51

你的意思是这样的吗?

public static class Extensions 
{ 
    public static IEnumerable<Exception> GetInnerExceptions(this Exception ex) 
    { 
     if (ex == null) 
     { 
      throw new ArgumentNullException("ex"); 
     } 

     var innerException = ex; 
     do 
     { 
      yield return innerException; 
      innerException = innerException.InnerException; 
     } 
     while (innerException != null); 
    } 
} 

,你可以在LINQ你的整个的异常层次结构,像这样通过这种方式:

exception.GetInnerExceptions().Where(e => e.Message == "Oops!"); 
+1

它不起作用。 – derek 2017-01-14 02:01:26

+4

@derek说“它不工作”。不是很有帮助。 – Tagc 2017-02-13 16:38:03

+3

这是工作 – 2017-03-03 14:42:42

8

LINQ一般是使用对象的集合工作。然而,可以说,在你的情况下,没有对象的集合(但是图形)。所以尽管有一些LINQ代码可能,恕我直言,它会是相当复杂或人为的。

另一方面,您的示例看起来像扩展方法实际上是合理的主要示例。不说话的问题,如重复使用,封装等

我会留在扩展方法,虽然我可能已经实现了这样的说法:

public static string GetAllMessages(this Exception ex) 
{ 
    if (ex == null) 
    throw new ArgumentNullException("ex"); 

    StringBuilder sb = new StringBuilder(); 

    while (ex != null) 
    { 
     if (!string.IsNullOrEmpty(ex.Message)) 
     { 
     if (sb.Length > 0) 
      sb.Append(" "); 

     sb.Append(ex.Message); 
     } 

     ex = ex.InnerException; 
    } 

    return sb.ToString(); 
} 

但是,这在很大程度上是口味问题。

4

我不这么认为,异常不是IEnumerable,所以你不能对它自己执行一个linq查询。

扩展方法返回的内部异常会像这样工作

public static class ExceptionExtensions 
{ 
    public static IEnumerable<Exception> InnerExceptions(this Exception exception) 
    { 
     Exception ex = exception; 

     while (ex != null) 
     { 
      yield return ex; 
      ex = ex.InnerException; 
     } 
    } 
} 

,那么你可以使用LINQ查询像这样附加的所有消息:

var allMessageText = string.Concat(exception.InnerExceptions().Select(e => e.Message + ",")); 
2
public static class ExceptionExtensions 
{ 
    public static IEnumerable<Exception> GetAllExceptions(this Exception ex) 
    { 
     Exception currentEx = ex; 
     yield return currentEx; 
     while (currentEx.InnerException != null) 
     { 
      currentEx = currentEx.InnerException; 
      yield return currentEx; 
     } 
    } 

    public static IEnumerable<string> GetAllExceptionAsString(this Exception ex) 
    {    
     Exception currentEx = ex; 
     yield return currentEx.ToString(); 
     while (currentEx.InnerException != null) 
     { 
      currentEx = currentEx.InnerException; 
      yield return currentEx.ToString(); 
     }    
    } 

    public static IEnumerable<string> GetAllExceptionMessages(this Exception ex) 
    { 
     Exception currentEx = ex; 
     yield return currentEx.Message; 
     while (currentEx.InnerException != null) 
     { 
      currentEx = currentEx.InnerException; 
      yield return currentEx.Message; 
     } 
    } 
} 
23

如何这个代码:

private static string GetExceptionMessages(this Exception e, string msgs = "") 
{ 
    if (e == null) return string.Empty; 
    if (msgs == "") msgs = e.Message; 
    if (e.InnerException != null) 
    msgs += "\r\nInnerException: " + GetExceptionMessages(e.InnerException); 
    return msgs; 
} 

用法:

输出的10
Console.WriteLine(e.GetExceptionMessages()) 

实施例:

有没有端点在http://nnn.mmm.kkk.ppp:8000/routingservice/router可能接受该消息监听。这通常是由不正确的地址或SOAP操作引起的。有关更多详细信息,请参阅InnerException(如果存在)。

的InnerException:无法连接到远程服务器

的InnerException:无连接可以作出,因为目标机器积极地拒绝它127.0.0.1:8000

+1

就我个人而言,我是递归的狂热粉丝。良好的用途。 – 2016-07-01 21:29:02

+3

你应该考虑在这里使用'StringBuilder'。当在空引用上调用时,IMO扩展方法也应该抛出'NullReferenceException'。 – dstarkowski 2016-11-23 15:47:52

+0

这个效果非常好!递归编程的一个非常合适的用例。 – Shiva 2017-07-22 03:55:44

4

要添加到别人,你可能希望让用户决定如何在信息中分离出来:

public static string GetAllMessages(this Exception ex, string separator = "\r\nInnerException: ") 
    { 
     if (ex.InnerException == null) 
      return ex.Message; 

     return ex.Message + separator + GetAllMessages(ex.InnerException, separator); 
    } 
4
public static string GetExceptionMessage(Exception ex) 
    { 
     if (ex.InnerException == null) 
     { 
      return string.Concat(ex.Message, System.Environment.NewLine, ex.StackTrace); 
     } 
     else 
     { 
      // Retira a última mensagem da pilha que já foi retornada na recursividade anterior 
      // (senão a última exceção - que não tem InnerException - vai cair no último else, retornando a mesma mensagem já retornada na passagem anterior) 
      if (ex.InnerException.InnerException == null) 
       return ex.InnerException.Message; 
      else 
       return string.Concat(string.Concat(ex.InnerException.Message, System.Environment.NewLine, ex.StackTrace), System.Environment.NewLine, GetExceptionMessage(ex.InnerException)); 
     } 
    } 
9

我知道这是显而易见的,但也许并不适用于所有。

exc.ToString(); 

这将通过你的所有内部异常和返回的所有消息,但堆在了一起跟踪等

+1

这很好,如果你乐意与所有与ToString爆炸的完整堆栈跟踪生活。这通常不适合上下文,例如,如果消息发送给用户。另一方面,消息不会给出内部异常消息(与执行递归的ToString不同)。我们最经常需要的是不存在的FullMessage,它是来自父级和内部例外的所有消息。 – Ricibob 2017-10-05 13:19:31