【问题标题】:How to check whether a Generic Type T is IEnumerable<T'> where T' is unkown如何检查通用类型 T 是否为 IEnumerable<T'> 其中 T' 未知
【发布时间】:2015-06-25 10:25:57
【问题描述】:

我写了一个这样的扩展方法:

public static String Join<T>(this IEnumerable<T> enumerable)
{
    if (typeof(T) is IEnumerable<T'> where T' is unknown at compile time)
    {
        return String.Join(",", enumerable.Select(e => e.Join()));
    }
    return String.Join(",", enumerable.Select(e => e.ToString()));
}

问题是我不知道如何在 if 语句中编写代码以使其工作。 有什么建议吗?谢谢!

【问题讨论】:

  • 您是否在寻找单一的嵌套IEnumerable 解决方案?
  • 嗨@YuvalItzchakov,我正在寻找的是一个无限的解决方案,嵌套可以更深。

标签: c# generics extension-methods


【解决方案1】:

因为您只调用.ToString(),您实际上并不真正关心T 是什么,只要它实现IEnumerable 与否。以下是如何在没有反射的情况下执行此操作,并且只使用IEnumerable 而不是IEnumerable&lt;T&gt;,我为String.Join 执行我自己的逻辑,因为它可以更轻松地编写递归逻辑。

internal static class ExtensionMethods
{
    public static String Join<T>(this IEnumerable<T> enumerable)
    {
        StringBuilder sb = new StringBuilder();
        JoinInternal(enumerable, sb, true);
        return sb.ToString();
    }

    private static bool JoinInternal(IEnumerable enumerable, StringBuilder sb, bool first)
    {
        foreach (var item in enumerable)
        {
            var castItem = item as IEnumerable;
            if (castItem != null)
            {
                first = JoinInternal(castItem, sb, first);
            }
            else
            {
                if (!first)
                {
                    sb.Append(",");
                }
                else
                {
                    first = false;
                }

                sb.Append(item);
            }
        }
        return first;
    }
}

Here is a test program 我写了这表明它一切正常(它测试类、结构和 IEnumerables 3 层深)。

编辑:根据您的评论,这里是另一个将嵌套的 IEnumerables 展平的版本,您可以在完成后对每个元素执行任何操作。

internal static class ExtensionMethods
{
    public static IEnumerable<T> SelectManyRecusive<T>(this IEnumerable enumerable)
    {
        foreach (var item in enumerable)
        {
            var castEnumerable = item as IEnumerable;
            if (castEnumerable != null 
                && ((typeof(T) != typeof(string)) || !(castEnumerable is string))) //Don't split string to char if string is our target
            {
                foreach (var inner in SelectManyRecusive<T>(castEnumerable))
                {
                    yield return inner;
                }
            }
            else
            {
                if (item is T)
                {
                    yield return (T)item;
                }
            }
        }
    }
}

我还遇到了一个错误,我认为这可能会影响我回答的第一部分,string 在技术上是IEnumerable&lt;char&gt;,所以IEnumerable&lt;string&gt; 也可以被视为IEnumerable&lt;IEnumerable&lt;char&gt;&gt; 并且它可能会放入太多,。第二个版本对此进行了检查。

Test program 展示了如何使用此方法和String.Join 一起使用。

【讨论】:

  • 谢谢,虽然不是那么“通用”,但是对于ToString的问题,确实不错,也够简单
  • @huangcd “这不是通用的”是什么意思?
  • @huangcd 我添加了一个更新,它为您提供了一个类似SelectMany 的方法,该方法将递归地沿着 IEnumerable 链向下展开,将其展平为单个类型的 IEnumerable。它像OfType&lt;T&gt;() linq 方法一样工作,过滤掉不匹配的项目。
  • 感谢@ScottChamberlain!解决了我的问题
【解决方案2】:

您可以使用返回对象的IEnumerable 的非通用版本。

public static String Join(this IEnumerable enumerable)
{
    var enumerable2 = enumerable as IEnumerable<IEnumerable>;
    if (enumerable2 != null)
    {
        return String.Join(",", enumerable2.Select(e => e.Join()));
    }
    return String.Join(",", enumerable.Select(e => e.ToString()));
}

编辑:该方法不需要是通用的。请注意,字符串是 IEnumerable,因此您可能需要添加一个特殊情况:

public static String Join(this IEnumerable enumerable)
{
    var stringEnumerable = enumerable as IEnumerable<string>;
    if (stringEnumerable != null)
    {
        return String.Join(",", stringEnumerable);
    }
    var enumerable2 = enumerable as IEnumerable<IEnumerable>;
    if (enumerable2 != null)
    {
        return String.Join(",", enumerable2.Select(e => e.Join()));
    }
    return String.Join(",", enumerable.Select(e => e.ToString()));
}

【讨论】:

  • 这适用于单个嵌套的IEnumerable。如果嵌套更深,这将不起作用。
  • 我认为这段代码不能通过编译器,我需要在Join上定义一个非泛型版本才能使其工作。
【解决方案3】:

“检查”部分问题不大,但随后对Join 的调用需要您提供类型参数。因此,我找到的唯一解决方案是使用反射来进行调用。

完整代码如下 (将类型参数检索到IEnumerable&lt;&gt; 的函数比这里需要的更通用,因为我只是从项目中复制粘贴它):

    static public Type[] ListeTypeArgumentZuBaseOderInterface(
        this Type Type,
        Type BaseGenericTypeDefinition)
    {
        if (null == Type || null == BaseGenericTypeDefinition)
        {
            return null;
        }

        if (BaseGenericTypeDefinition.IsInterface)
        {
            var MengeInterface = Type.GetInterfaces();

            if (null != MengeInterface)
            {
                foreach (var Interface in MengeInterface)
                {
                    if (!Interface.IsGenericType)
                    {
                        continue;
                    }

                    var InterfaceGenericTypeDefinition = Interface.GetGenericTypeDefinition();

                    if (!InterfaceGenericTypeDefinition.Equals(BaseGenericTypeDefinition))
                    {
                        continue;
                    }

                    return Interface.GenericTypeArguments;
                }
            }
        }
        else
        {
            var BaseTypeAktuel = Type;

            while (null != BaseTypeAktuel)
            {
                if (BaseTypeAktuel.IsGenericType)
                {
                    var BaseTypeGenericTypeDefinition = BaseTypeAktuel.GetGenericTypeDefinition();

                    if (BaseTypeGenericTypeDefinition.Equals(BaseGenericTypeDefinition))
                    {
                        return BaseTypeAktuel.GenericTypeArguments;
                    }
                }

                BaseTypeAktuel = BaseTypeAktuel.BaseType;
            }
        }

        return null;
    }

    static public Type IEnumerableTypeArgumentExtrakt(
        this Type TypeImplementingEnumerable)
    {
        var GenericTypeArguments =
            ListeTypeArgumentZuBaseOderInterface(TypeImplementingEnumerable, typeof(IEnumerable<>));

        if (null == GenericTypeArguments)
        {
            //  does not implement IEnumerable<>
            return null;
        }

        return GenericTypeArguments.FirstOrDefault();
    }

    public static String Join<T>(this IEnumerable<T> enumerable)
    {
        //  ¡the typeof() has to refer to the class containing this Method!:
        var SelfType = typeof(Extension);

        var IEnumerableTypeArgument = IEnumerableTypeArgumentExtrakt(typeof(T));

        if (null != IEnumerableTypeArgument)
        {
            System.Reflection.MethodInfo method = SelfType.GetMethod("Join");
            System.Reflection.MethodInfo generic = method.MakeGenericMethod(IEnumerableTypeArgument);

            return String.Join(",", enumerable.Select(e => generic.Invoke(null, new object[] { e })));
        }

        return String.Join(",", enumerable.Select(e => e.ToString()));
    }

【讨论】:

    猜你喜欢
    • 2014-03-07
    • 2018-02-09
    • 1970-01-01
    • 2011-04-13
    • 2016-09-23
    • 2016-05-01
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    相关资源
    最近更新 更多