简体   繁体   English

如何检查泛型类型T是否为IEnumerable <T'> 其中T&#39;未知

[英]How to check whether a Generic Type T is IEnumerable<T'> where T' is unkown

I've written an extension method like this: 我写了一个像这样的扩展方法:

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

The problem is I don't know how to write code inside the if statement to make it works. 问题是我不知道如何在if语句中编写代码使其工作。 Any suggestion? 有什么建议吗? Thanks! 谢谢!

Because you are only calling .ToString() you actually don't really care what T is, only if it implements IEnumerable or not. 因为你只是在调用.ToString()你实际上并不关心T是什么,只有它实现了IEnumerable Here is how to do it without reflection and just using IEnumerable instead of IEnumerable<T> , I do my own logic for String.Join because it made it easier to code the recursive logic. 下面是如何在没有反射的情况下完成它,只使用IEnumerable而不是IEnumerable<T> ,我为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 I wrote that shows it all works (it tests classes, structs, and IEnumerables 3 layers deep). 这是我编写的一个测试程序 ,它显示了它的全部功能(它测试类,结构和IEnumerables 3层深)。

EDIT: Per your comment here is another version that flattens out the nested IEnumerables, you can do whatever you want to each element when you are done. 编辑:根据你的评论这里是另一个版本,扁平化嵌套的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;
                }
            }
        }
    }
}

There also was a bug I ran in to that I think may afffect my first part of my answer, a string is technically a IEnumerable<char> so a IEnumerable<string> could be also seen as a IEnumerable<IEnumerable<char>> and it may put too many , in. This second version has a check for that. 还有一个我遇到的错误,我认为可能会影响我的第一部分答案,一个string在技​​术上是一个IEnumerable<char>所以IEnumerable<string>也可以看作IEnumerable<IEnumerable<char>>它可能把太多,英寸,这第二个版本有一个检查。

Test program showing how to use this method and String.Join together. 测试程序显示如何使用此方法和String.Join一起使用。

You can use the non generic version of IEnumerable that returns objects. 您可以使用返回对象的非通用版本的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()));
}

Edit : the method doesn't need to be generic. 编辑:该方法不需要是通用的。 Beware that string is IEnumerable so you might want to add a special case : 请注意字符串是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()));
}

The "check" part is not much of a problem but the subsequent call to Join requires you to supply a Type Argument. “检查”部分没有太大问题,但随后对Join调用要求您提供Type参数。 Therefore, the only solution I found uses reflection to make that call. 因此,我发现的唯一解决方案是使用反射来进行调用。

Complete code as follows (the function to retrieve the Type Argument to IEnumerable<> is more generic than needed here because I just copy&pasted it from a project): 完整的代码如下(将类型参数检索到IEnumerable<>的函数比这里需要的更通用,因为我只是从项目中复制并粘贴它):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM