2015-06-02 88 views
0

我想有一個擴展方法來打印X>如何將泛型參數轉換爲泛型接口?

public static IEnumerable<T> log_elements<T>(this IEnumerable<T> collection, bool recursive = true) 
{ 
    Form1.log("["); 
    foreach (T i in collection) 
     if(recursive&&(i.GetType().GetGenericTypeDefinition()==typeof(IEnumerable<>))) 
      (IEnumerable<>)i.log_elements();//This doesn't work 
     else 
      Form1.log(i); 
    Form1.log("]"); 
    return collection; 
} 

的IEnumerable <的所有內容如果它包含了IEnumerable < Y>,該方法應該要求它。

我不能添加log_elements<T>(this IEnumerable<IEnumerable<T>>,bool),因爲IEnumerable<T>匹配原始方法的T

我幾乎可以肯定,應該有一個在c#中這樣簡單的問題的解決方案。

+2

看來您實際上並不需要「IEnumerable」的通用形式。 –

回答

2

切換到非通用IEnumerable(其中仿製藥從反正繼承)。

public static IEnumerable<T> log_elements<T>(this IEnumerable<T> collection, bool recursive = true) 
{ 
    logElementsInternal(collection, recursive); 
    return collection; 
} 

private static void logElementsInternal(IEnumerable collection, bool recursive) 
{ 
    Form1.log("["); 
    foreach (var i in collection) 
     if(recursive && i is IEnumerable) 
      logElementsInternal((IEnumerable)i); 
     else 
      Form1.log(i); 
    Form1.log("]"); 
} 
+0

謝謝!但函數不能放在linq表達式中,比如'x.Select(...)。log_elements()。哪裏(...);'可以嗎? – user2136963

+0

不,但你可以有一個通用函數,只是調用這個。我會更新。 – Magnus

1

編輯說明:
這不會解決IEnumerable,但省略了反射的需要。 此代碼的主要目標是「容易」理解語法並儘可能重用。

如果你確實想要遞歸下面的代碼應該滿足你的目的;

/// <summary> 
/// Returns the top level items including all their recursive descendants. 
/// </summary> 
/// <typeparam name="T">The generic enumerable type parameter.</typeparam> 
/// <param name="source">The source to traverse.</param> 
/// <param name="childPropertyExpression">The recursive property expression.</param> 
/// <returns>IEnumerable(<typeparamref name="T"/>)</returns> 
public static IEnumerable<T> IncludeDescendants<T>(this IEnumerable<T> source, Expression<Func<T, IEnumerable<T>>> childPropertyExpression) 
{ 
    // The actual recursion is processed inside the private static method 
    // This method is serving the purpose of writing expressions. 
    var items = IncludeDescendants(source, childPropertyExpression.Compile()); 
    foreach (var item in items) 
    { 
     yield return item; 
    } 
} 

private static IEnumerable<T> IncludeDescendants<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> childPropertyFunc) 
{ 
    foreach (var item in source) 
    { 
     yield return item; 
     var subSource = childPropertyFunc.Invoke(item); 
     if (subSource != null) 
     { 
      foreach (var subItem in IncludeDescendants(subSource, childPropertyFunc)) 
      { 
       yield return subItem; 
      } 
     } 
    } 
} 

用法:

var allChildrenRecursive = MyObject.Children.IncludeDescendants(c => c.Children); 
foreach(var child in allChildrenRecursive) 
{ 
    Log(child); 
} 

在這種情況下,每個孩子都有相同類型的兒童的遞歸集合,因爲它不依賴於變量的方法範圍之外更可重複使用。
請注意循環引用,因爲在這種情況下,只要您的堆棧允許,就會發生這種情況。 (StackOverflowException)。從IEnumerable<T>

+0

雖然很有用,但如果輸入「IEnumerable」,它將自動迭代迭代集合中的項目。 – Magnus

+0

@Magnus儘管我的答案並沒有直接回答這個問題,這個問題更多地強調遞歸而不是反思。但我會更新我的答案並附上關於此的說明。 – Silvermind

+0

那麼,你不需要使用反射來做到這一點。 – Magnus

-1
var a = i as IEnumerable<T>; 
(IEnumerable<T>) a.log_elements(true); 
+0

'我'可能'IEnumerable < T2 >'並將其轉換爲'IEnumerable < T >'會引起異常 – user2136963