将IsAssignableFrom与“开放”通用类型一起使用


73

使用反射,我试图找到从给定基类继承的一组类型。不需要很长时间就可以找到简单类型,但是在泛型方面我感到很困惑。

对于这段代码,第一个IsAssignableFrom返回true,但是第二个返回false。但是,最终的任务编译就可以了。

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

那么,如何在运行时确定一个泛型类型定义是否派生自另一个泛型类型定义?


2
最终任务仅涉及generic2...
Daniel Hilgarth 2011年


@Daniel Hilgarth-谢谢!在发布之前清理示例代码时,我很想念它。分配为generic1 <class1>时仍会编译cc = newgeneric2 <class1>();
ThatBlairGuy 2011年

确保编译;-)问题是,这generic1<>是不能分配给generic2<> 一般的,这就是你问什么,当你忽略在调用泛型参数typeof。只有分配,如果泛型参数是相同的generic1generic2。怎么解决呢?请参阅Konrad Rudolph的答案。
Daniel Hilgarth 2011年

Answers:


105

答案到另一个问题

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

(如果您喜欢答案,请替换链接的答案,因为该代码不是我的。)


谢谢!这正是我想要的。早些时候,我没有注意到其他问题。
ThatBlairGuy 2011年

2
在以下情况下,此方法可能是错误的:IsAssignableToGenericType(typeof(A <string>),typeof(A <>)); //返回false
sam sha

但是,这个答案很好,不过在使用时要当心。我使用了它,使布尔表达式起作用,然后我什么也不能做,因为我可以强制转换对象以便使用它……我必须引入一个接口,我可以用它简单地检查一下is……
Jaap

2
基于此方法,我创建了另一种方法来获取用于基本类型的通用参数:gist.github.com/2936304
Jaap

请注意,这比需要的开销更大,因为它会为每种基本类型重复一次接口列表,这是不必要的。
canton7 '20

12

您发布的确切代码不会返回令人惊讶的结果。

这说“假”:

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

这说“ true”:

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

区别在于开放的泛型类型不能具有实例,因此一个不能“分配”给另一个。

文档

返回true如果c和当前 Type表示相同的类型,或者当前Type处于的继承层次结构中c,或者当前Typec实现的接口,或者如果c是通用类型参数,并且当前 Type表示的约束之一,则返回返回cfalse如果这些条件都不为真,或者cnull

在这种情况下,显然这些条件都不成立。还有一点需要注意的是:

不能从封闭的构造类型中分配泛型类型定义。也就是说,不能将封闭的构造类型MyGenericList<int>MyGenericList(Of Integer)在Visual Basic中)分配给type 变量MyGenericList<T>


2
很好的解释!
Kiran Challa

3

在以下情况下,使用Konrad Rudolph提供的方法可能是错误的,例如:IsAssignableToGenericType(typeof(A),typeof(A <>)); //返回false

我认为这是一个更好的答案

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

测试用例,请参阅将IsAssignableFrom与C#泛型一起使用以了解详细信息

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{}
}

2
注意:此答案不处理通用接口。
phloopy


0

我有另一种方法可以解决此问题,这是我的课程

public class Signal<T>{
   protected string Id {get; set;} //This must be here, I use a property because MemberInfo is returned in an array via GetMember() reflection function
   //Some Data and Logic And stuff that involves T
}

public class OnClick : Signal<string>{}

现在,如果我有一个OnClick类型的实例,但我不知道,我想确定我是否有一个实例继承自任何类型的Signal <>?我做这个

Type type = GetTypeWhomISuspectMightBeAGenericSignal();

PropertyInfo secretProperty = type.GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance);

Type SpecificGenericType = secretProperty.DeclaringType; //This is the trick

bool IsMyTypeInheriting = SpecificGenericType.IsGenericType && SpecificGenericType.GetGenericTypeDefinition() == typeof(Signal<>); //This way we are getting the genericTypeDefinition and comparing it to any other genericTypeDefinition of the same argument length.

所以这对我有用,它不是递归的,它通过指定的属性使用了一个技巧。它的局限性在于难以编写一个功能来检查所有泛型的可分配性。但是对于特定类型它可以工作

显然,您需要检查if()的条件是否更好以及是否合适,但这是评估这种类型对其基本泛型的可分配性所需的Raw行。

希望这可以帮助


0

我的两分钱。恕我直言,分离IsAssignableFrom的实现,派生或原始功能没有多大意义,

根据先前给出的答案进行构建,这就是我的方法:

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.