2012-02-16 124 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