2015-06-02 4 views
0

Я хочу иметь метод расширения для печати всех содержимого IEnumerable < 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 < Y>, метод должен быть вызван для него тоже.

Я не могу добавить log_elements<T>(this IEnumerable<IEnumerable<T>>,bool), потому что IEnumerable<T> соответствует T оригинального метода.

Я почти уверен, что должно быть решение для такой простой проблемы в C#.

+2

Не похоже, что вам вообще нужна общая форма 'IEnumerable'. –

ответ

2

Переход от IEnumerable<T> к не родового 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).

+0

Хотя это полезно, он автоматически автоматически рекурсивно перебирает элементы в коллекции, если тип 'IEnumerable'. – Magnus

+0

@ Magnus Наверху мой ответ не отвечает на вопрос напрямую; в вопросе больше внимания уделялось рекурсии, чем отражению. Но я уточню свой ответ с запиской об этом. – Silvermind

+0

Ну, вам не нужно использовать отражение, чтобы сделать это. – Magnus

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

'i' может быть' IEnumerable < T2 >' и отбрасывать его на 'IEnumerable < T >' вызовет исключение – user2136963

Смежные вопросы