87 votes

Utilisation d'IsAssignableFrom avec des types génériques « ouverts »

En utilisant la réflexion, j'essaie de trouver l'ensemble des types qui héritent d'une classe de base donnée. Il n'a pas fallu longtemps pour comprendre pour les types simples, mais je suis stumped quand il s'agit de génériques.

Pour ce morceau de code, le premier IsAssignableFrom retourne true, mais le second retourne false. Et pourtant, la tâche finale se compile très bien.

class class1 { }
class class2 : class1 { }
class generic1<T> { }
class generic2<T> : generic1<T> { }

class Program
{
    static void Main(string[] args)
    {
        Type c1 = typeof(class1);
        Type c2 = typeof(class2);
        Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2));

        Type g1 = typeof(generic1<>);
        Type g2 = typeof(generic2<>);
        Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));

        generic1<class1> cc = new generic2<class1>();
    }
}

Alors, comment puis-je déterminer au moment de l'exécution si une définition de type générique est dérivée d'une autre ?

123voto

Konrad Rudolph Points 231505

De la réponse à une autre question :

public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
    var interfaceTypes = givenType.GetInterfaces();

    foreach (var it in interfaceTypes)
    {
        if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
            return true;
    }

    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
        return true;

    Type baseType = givenType.BaseType;
    if (baseType == null) return false;

    return IsAssignableToGenericType(baseType, genericType);
}

(Si vous aimez la réponse s'il vous plaît mettre à jour la réponse liée puisque le code n'est pas le mien.)

3voto

sam sha Points 335

Dans le cas suivant, utiliser la méthode fournie par Konrad Rudolph pourrait être erronée, comme :IsAssignableToGenericType (typeof(A), typeof(A<>)) ;// return false

Je pense qu'il y a une meilleure réponse

public static bool IsAssignableFrom(Type extendType, Type baseType)
{
    while (!baseType.IsAssignableFrom(extendType))
    {
        if (extendType.Equals(typeof(object)))
        {
            return false;
        }
        if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
        {
            extendType = extendType.GetGenericTypeDefinition();
        }
        else
        {
            extendType = extendType.BaseType;
        }
    }
    return true;
}

le cas de test, voir Utiliser IsAssignableFrom avec C# generics pour plus de détails

using System;

/**
 * Sam Sha - yCoder.com
 *
 * */
namespace Test2
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            string a = "ycoder";
            Console.WriteLine(a is object);
            A aa = new A();
            //Console.WriteLine(aa is A<>);//con't write code like this
            typeof(A<>).IsAssignableFrom(aa.GetType());//return false

            Trace(typeof(object).IsAssignableFrom(typeof(string)));//true
            Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false

            AAA aaa = new AAA();
            Trace("Use IsTypeOf:");
            Trace(IsTypeOf(aaa, typeof(A<>)));
            Trace(IsTypeOf(aaa, typeof(AA)));
            Trace(IsTypeOf(aaa, typeof(AAA<>)));

            Trace("Use IsAssignableFrom from stackoverflow - not right:");
            Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error
            Trace(IsAssignableFrom(typeof(AA), typeof(A<>)));
            Trace(IsAssignableFrom(typeof(AAA), typeof(A<>)));

            Trace("Use IsAssignableToGenericType:");
            Trace(IsAssignableToGenericType(typeof(A), typeof(A<>)));
            Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>)));
            Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>)));
        }

        static void Trace(object log){
                Console.WriteLine(log);
        }

        public static bool IsTypeOf(Object o, Type baseType)
        {
            if (o == null || baseType == null)
            {
                return false;
            }
            bool result = baseType.IsInstanceOfType(o);
            if (result)
            {
                return result;
            }
            return IsAssignableFrom(o.GetType(), baseType);
        }

        public static bool IsAssignableFrom(Type extendType, Type baseType)
        {
            while (!baseType.IsAssignableFrom(extendType))
            {
                if (extendType.Equals(typeof(object)))
                {
                    return false;
                }
                if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
                {
                    extendType = extendType.GetGenericTypeDefinition();
                }
                else
                {
                    extendType = extendType.BaseType;
                }
            }
            return true;
        }

        //from stackoverflow - not good enough
        public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
            var interfaceTypes = givenType.GetInterfaces();

            foreach (var it in interfaceTypes)
                if (it.IsGenericType)
                    if (it.GetGenericTypeDefinition() == genericType) return true;

            Type baseType = givenType.BaseType;
            if (baseType == null) return false;

            return baseType.IsGenericType &&
                baseType.GetGenericTypeDefinition() == genericType ||
                IsAssignableToGenericType(baseType, genericType);
        }
    }

    class A{}
    class AA : A{}
    class AAA : AA{}
}

1voto

João Sequeira Points 18

Mes deux cents. L'OMI n'a pas beaucoup de sens de séparer les outils, les dérivés ou la fonctionnalité originale d'IsAssignableFrom,

En construisant à partir des réponses données précédemment, voici comment je le fais :

public static bool ImplementsOrDerives(this Type @this, Type from)
{
    if(from is null)
    {
        return false;
    }
    else if(!from.IsGenericType)
    {
        return from.IsAssignableFrom(@this);
    }
    else if(!from.IsGenericTypeDefinition)
    {
        return from.IsAssignableFrom(@this);
    }
    else if(from.IsInterface)
    {
        foreach(Type @interface in @this.GetInterfaces())
        {
            if(@interface.IsGenericType && @interface.GetGenericTypeDefinition() == from)
            {
                return true;
            }
        }
    }

    if(@this.IsGenericType && @this.GetGenericTypeDefinition() == from)
    {
        return true;
    }

    return @this.BaseType?.ImplementsOrDerives(from) ?? false;
}

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