54 votes

Convertir / Cast IEnumerable en IEnumerable<T>

J'ai une classe (un contrôle web) qui a une propriété de type IEnumerable et je voudrais travailler avec le paramètre en utilisant LINQ.

Existe-t-il un moyen de couler / convertir / invoquer par réflexion vers IEnumerable<T> sans connaître le type au moment de la compilation ?

Method void (IEnumerable source)
{
    var enumerator = source.GetEnumerator();

    if (enumerator.MoveNext())
    {
        var type = enumerator.Current.GetType();
        Method2<type>(source); // this doesn't work! I know!
    }
}

void Method2<T>(IEnumerable<T> source) {}

65voto

Jon Skeet Points 692016

Est-ce que votre Method2 se préoccupe vraiment de son type ? Si non, vous pouvez simplement appeler Cast<object>() :

void Method (IEnumerable source)
{
    Method2(source.Cast<object>());
}

Si vous devez absolument obtenir le bon type, vous devrez utiliser la réflexion.

Quelque chose comme :

MethodInfo method = typeof(MyType).GetMethod("Method2");
MethodInfo generic = method.MakeGenericMethod(type);
generic.Invoke(this, new object[] {source});

Ce n'est pas idéal cependant... en particulier, si la source n'est pas exactement un IEnumerable<type> alors l'invocation échouera. Par exemple, si le premier élément est une chaîne de caractères, mais que la source est un fichier List<object> vous aurez des problèmes.

11voto

Brian Genisio Points 30777

Vous voulez probablement refactoriser votre code pour utiliser IEnumerable.Cast<T>

Utilisez-le comme ça :

IEnumerable mySet = GetData();
var query = from x in mySet.Cast<int>()
            where x > 2
            select x;

3voto

luksan Points 4063

Avec .NET 4, vous pouvez simplement lancer source a dynamic avant de le transmettre à la méthode. Ainsi, la surcharge générique correcte sera résolue au moment de l'exécution sans aucun code de réflexion affreux :

void Method(IEnumerable source)
{
    var enumerator = source.GetEnumerator();

    if (enumerator.MoveNext())
    {
        Method2((dynamic)source);
    }
}

Comme pour la deuxième solution de Jon, cela ne fonctionnera que si votre source est en fait une IEnumerable<T> . S'il s'agit d'un simple IEnumerable alors vous devrez créer une autre méthode qui le convertira dans le format correct IEnumerable<T> comme dans la solution suivante :

IEnumerable<T> Convert<T>(IEnumerable source, T firstItem)
{
    // Note: firstItem parameter is unused and is just for resolving type of T
    foreach(var item in source)
    {
        yield return (T)item;
    }
}

void Method(IEnumerable source)
{
    var enumerator = source.GetEnumerator();

    if (enumerator.MoveNext())
    {
        dynamic firstItem = enumerator.Current;
        dynamic typedEnumerable = Convert(source, firstItem);
        Method2(typedEnumerable);
    }
}

2voto

C'est des années plus tard, mais j'ai résolu la List<Object> problème.

void Method(IEnumerable source)
{
    var enumerator = source.GetEnumerator();
    if (enumerator.MoveNext())
    {
        MethodInfo method = typeof(MyClass).GetMethod("Method2");
        MethodInfo generic;
        Type type = enumerator.Current.GetType();
        bool sameType = true;

        while (enumerator.MoveNext())
        {
            if (enumerator.Current.GetType() != type)
            {
                sameType = false;
                break;
            }
        }

        if (sameType)
            generic = method.MakeGenericMethod(type);
        else
            generic = method.MakeGenericMethod(typeof(object));

        generic.Invoke(this, new object[] { source });
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X