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

Posted

技术标签:

【中文标题】将 IsAssignableFrom 与“开放”泛型类型一起使用【英文标题】:Using IsAssignableFrom with 'open' generic types 【发布时间】:2011-07-24 14:16:52 【问题描述】:

使用反射,我试图找到从给定基类继承的类型集。很快就弄清楚了简单的类型,但是当涉及到泛型时我被难住了。

对于这段代码,第一个 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>();
    

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

【问题讨论】:

最终作业只涉及generic2... How To Detect If Type is Another Generic Type 的可能重复项 @Daniel Hilgarth - 谢谢!当我在发布之前清理示例代码时,我错过了这一点。当赋值为 generic1 cc = new generic2(); 时它仍然可以编译 当然可以编译 ;-) 问题是,generic1&lt;&gt; 不能分配给 generic2&lt;&gt;一般,这就是你要问的,当你省略对typeof 的调用中的通用参数。只有当generic1generic2 的通用参数相同时,它才可赋值。如何解决?请参阅康拉德鲁道夫的回答。 【参考方案1】:

@konrad_ruldolph 的回答大部分是正确的,但它要求您知道基本类型/接口是开放的泛型。我提出了一项改进,将非泛型测试与循环相结合来测试泛型匹配。

    public static class Ext
    
        public static bool IsAssignableToGeneric(
            this Type assignableFrom,
            Type assignableTo)
        
            bool IsType(Type comparand)
                => assignableTo.IsAssignableFrom(comparand)
                    || (comparand.IsGenericType
                    && comparand.GetGenericTypeDefinition() == assignableTo);

            while (assignableFrom != null)
            
                if (IsType(assignableFrom)
                    || assignableFrom
                    .GetInterfaces()
                    .Any(IsType))
                
                    return true;
                

                assignableFrom = assignableFrom.BaseType;
            

            return false;
        
    

【讨论】:

【参考方案2】:

我的两分钱。恕我直言,分离 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;

【讨论】:

【参考方案3】:

我有一个不同的方法可以解决这个问题,这是我的课程

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() 条件和其他东西,但这些是评估类型可分配给其基本泛型所需的原始行,以这种方式。

希望对你有帮助

【讨论】:

【参考方案4】:

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

这说的是“假”:

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

这说的是“真的”:

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

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

来自docs:

返回 true 如果 c 和当前 Type 代表相同的类型,或者如果 当前的Typec 的继承层次结构,或者如果 当前的Type 是一个接口 c 实现,或者如果 c 是 泛型类型参数和当前 Type 代表其中之一 c 的约束。 false 如果没有 这些条件为真,或者如果cnull

在这种情况下,显然这些条件都不成立。还有一个额外的说明:

泛型类型定义不是 可从封闭构造分配 类型。也就是说,您不能分配 封闭构造型 MyGenericList&lt;int&gt;(在 Visual Basic 中为 MyGenericList(Of Integer))到 MyGenericList&lt;T&gt; 类型的变量。

【讨论】:

很好的解释!【参考方案5】:

来自answer to another 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);

(如果您喜欢这个答案,请为链接的答案投票,因为代码不是我的。)

【讨论】:

谢谢!这正是我想要的。我之前看的时候没有注意到另一个问题。 在以下情况下,这个方法可能是错误的:IsAssignableToGenericType(typeof(A), typeof(A));// return false 不过这个答案很棒,但是当你使用它时要小心。我使用了它,使 bool 表达式起作用,然后我什么也做不了,因为我可以强制转换我的对象以使用它...我必须引入一个接口,我可以简单地使用 is 进行检查... 基于这个方法,我创建了一个不同的方法来获取用于基本类型的通用参数:gist.github.com/2936304 请注意,这比它需要的更昂贵,因为它为每个基本类型迭代接口列表一次,这是不必要的。【参考方案6】:

在以下情况下,使用 Konrad Rudolph 提供的方法可能是错误的,例如:IsAssignableToGenericType(typeof(A), typeof(A));// return 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;

测试用例,详见Using IsAssignableFrom with C# generics

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 *** - 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 *** - 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

【讨论】:

注意:这个答案不处理通用接口。【参考方案7】:

您需要比较包含的类型。见:How to get the type of T from a member of a generic class or method?

换句话说,我认为您需要检查泛型类所包含的类型是否是可分配的,而不是泛型类本身。

【讨论】:

以上是关于将 IsAssignableFrom 与“开放”泛型类型一起使用的主要内容,如果未能解决你的问题,请参考以下文章

instanceof 与isAssignableFrom

腾讯云联手创梦天地打造开放泛娱乐平台

Class.isAssignableFrom与instanceof的区别

Java isAssignableFrom()方法与instanceof()方法区别

Java isAssignableFrom()方法与instanceof()方法区别

Java isAssignableFrom()方法与instanceof()方法区别