如何将通用参数转换为通用接口?

How to cast generic parameter to generic interface?

我想要一个扩展方法来打印 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# 中应该有针对这种简单问题的解决方案。

编辑备注:
这不解决 IEnumerable,但省略了反射的需要。 此代码的主要目标是 'easy' 理解语法并尽可能重用。

如果你真的想要递归,下面的代码应该可以满足你的目的;它更可重用,因为它不依赖于方法范围之外的变量:

/// <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);
}

在这种情况下,每个 child 都有一个相同类型 children 的递归集合。
当心循环引用,因为在这种情况下,这将达到您的堆栈允许的范围。 (WhosebugException).

var a = i as IEnumerable<T>;
(IEnumerable<T>) a.log_elements(true);

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("]");
}