【问题标题】:Using IsAssignableFrom with 'open' generic types将 IsAssignableFrom 与“开放”泛型类型一起使用
【发布时间】: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...
  • @Daniel Hilgarth - 谢谢!当我在发布之前清理示例代码时,我错过了这一点。当赋值为 generic1 cc = new generic2(); 时它仍然可以编译
  • 当然可以编译 ;-) 问题是,generic1&lt;&gt; 不能分配给 generic2&lt;&gt;一般,这就是你要问的,当你省略对typeof 的调用中的通用参数。只有当generic1generic2 的通用参数相同时,它才可赋值。如何解决?请参阅康拉德鲁道夫的回答。

标签: c# generics reflection types


【解决方案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 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{}
        }
        

        【讨论】:

        • 注意:这个答案不处理通用接口。
        【解决方案7】:

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

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

        【讨论】:

          猜你喜欢
          • 2015-09-12
          • 2010-09-08
          • 2020-02-29
          • 2014-08-06
          • 1970-01-01
          • 2015-10-23
          相关资源
          最近更新 更多