Check if types are castable / subclasses

后端 未结 6 2228
执笔经年
执笔经年 2020-12-07 00:32

I have they type of two members as strings - and not as a Type instance. How can I check if the two types are castable? Let\'s say string one is \"System.Windows.Forms.Label

相关标签:
6条回答
  • 2020-12-07 01:00

    How about:

    public bool IsCastable(String type0, String type1)
    {
      return Type.GetType(type1).IsAssignableFrom(Type.GetType(type0));
    }
    
    0 讨论(0)
  • 2020-12-07 01:09

    If you can convert these strings to Type objects then your best bet is Type.IsAssignableFrom.

    Beware though, this only tells you if two Type instances are compatible at a CLR level. This will not take into account such things as user defined conversions or other C# semantics.

    0 讨论(0)
  • 2020-12-07 01:13

    I was helped by this discussion, Thanks.

    I modified nawfal's code to solve problem about primitive types.

    Now it returns correct results.

    typeof(short).IsCastableTo(typeof(int)); // True
    typeof(short).IsCastableTo(typeof(int), implicitly:true); // True
    typeof(int).IsCastableTo(typeof(short)); // True
    typeof(int).IsCastableTo(typeof(short), implicitly:true); // False
    

    The code is as below.

    public static bool IsCastableTo(this Type from, Type to, bool implicitly = false)
    {
        return to.IsAssignableFrom(from) || from.HasCastDefined(to, implicitly);
    }
    
    static bool HasCastDefined(this Type from, Type to, bool implicitly)
    {
        if ((from.IsPrimitive || from.IsEnum) && (to.IsPrimitive || to.IsEnum))
        {
            if (!implicitly)
                return from==to || (from!=typeof(Boolean) && to!=typeof(Boolean));
    
            Type[][] typeHierarchy = {
                new Type[] { typeof(Byte),  typeof(SByte), typeof(Char) },
                new Type[] { typeof(Int16), typeof(UInt16) },
                new Type[] { typeof(Int32), typeof(UInt32) },
                new Type[] { typeof(Int64), typeof(UInt64) },
                new Type[] { typeof(Single) },
                new Type[] { typeof(Double) }
            };
            IEnumerable<Type> lowerTypes = Enumerable.Empty<Type>();
            foreach (Type[] types in typeHierarchy)
            {
                if ( types.Any(t => t == to) )
                    return lowerTypes.Any(t => t == from);
                lowerTypes = lowerTypes.Concat(types);
            }
    
            return false;   // IntPtr, UIntPtr, Enum, Boolean
        }
        return IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, implicitly, false)
            || IsCastDefined(from, _ => to, m => m.ReturnType, implicitly, true);
    }
    
    static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType,
                            Func<MethodInfo, Type> derivedType, bool implicitly, bool lookInBase)
    {
        var bindinFlags = BindingFlags.Public | BindingFlags.Static
                        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly);
        return type.GetMethods(bindinFlags).Any(
            m => (m.Name=="op_Implicit" || (!implicitly && m.Name=="op_Explicit"))
                && baseType(m).IsAssignableFrom(derivedType(m)));
    }
    
    0 讨论(0)
  • 2020-12-07 01:20

    It might seem like you should use Type.IsAssignableFrom but note carefully the documentation:

    public virtual bool IsAssignableFrom(Type c)

    true if c and the current [instance of] Type represent the same type, or if the current [instance of] Type is in the inheritance hierarchy of c, or if the current [instance of] Type is an interface that c implements, or if c is a generic type parameter and the current [instance of] Type represents one of the constraints of c. false if none of these conditions are true, or if c is a null reference (Nothing in Visual Basic).

    In particular:

    class Base { }
    clase NotABase { public static implicit operator Base(NotABase o) { // } }
    
    Console.WriteLine(typeof(Base).IsAssignableFrom(typeof(NotABase)));
    

    will print False on the console even though NotABases are implicitly castable to Bases. So, to handle casting, we could use reflection like so:

    static class TypeExtensions {
        public static bool IsCastableTo(this Type from, Type to) {
            if (to.IsAssignableFrom(from)) {
                return true;
            }
            return from.GetMethods(BindingFlags.Public | BindingFlags.Static)
                              .Any(
                                  m => m.ReturnType == to && 
                                       (m.Name == "op_Implicit" || 
                                        m.Name == "op_Explicit")
                              );
        }
    }
    

    Usage:

    Console.WriteLine(typeof(string).IsCastableTo(typeof(int))); // false
    Console.WriteLine(typeof(NotABase).IsCastableTo(typeof(Base))); // true
    

    And for your case

    // from is string representing type name, e.g. "System.Windows.Forms.Label"
    // to is string representing type name, e.g. "System.Windows.Forms.Control"
    Type fromType = Type.GetType(from);
    Type toType = Type.GetType(to);
    bool castable = from.IsCastableTo(to);
    
    0 讨论(0)
  • 2020-12-07 01:25

    This is same as Jason's answer, but solves some problems with his solution.

    public static bool IsCastableTo(this Type from, Type to)
    {
        return to.IsAssignableFrom(from)
            || to.GetConvertOperators().Any(m => m.GetParameters()[0].ParameterType.IsAssignableFrom(from))
            || from.GetConvertOperators(true).Any(m => to.IsAssignableFrom(m.ReturnType));
    }
    
    public static IEnumerable<MethodInfo> GetConvertOperators(this Type type, bool lookInBase = false)
    {
        var bindinFlags = BindingFlags.Public
                        | BindingFlags.Static
                        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly);
        return type.GetMethods(bindinFlags).Where(m => m.Name == "op_Implicit" || m.Name == "op_Explicit");
    }
    

    This should handle situations that arise due to inheritance as well. For instance:

    class Mammal { public static implicit operator Car (Mammal o) { return null; } }
    class Cow : Mammal { }
    class Vehicle { }
    class Car : Vehicle { }
    

    Here the implicit relationship is between Mammal and Car, but since Cow is Mammal as well, there exist an implicit conversion from Cow to Car. But all Cars are Vehicles; hence a Cow would go into a Vehicle.

    Cow c = null; 
    Vehicle v = c; //legal
    

    So

    typeof(Cow).IsCastableTo(typeof(Vehicle)); //true 
    

    prints true, even though no direct convert operator exist between Cow and Vehicle.

    The solution above fails for primitive types where the conversion is built directly into the language than the framework, so something like

    typeof(short).IsCastableTo(typeof(int));
    

    fails. Afaik, only manually handling will help. You will get complete list of implicit and explicit conversion for numeric types and other primitive types from msdn.

    Edit:

    The IsCastableTo function could be little more "DRY" perhaps at the cost of being less readable, but I like it :)

    public static bool IsCastableTo(this Type from, Type to)
    {
        return to.IsAssignableFrom(from)
            || IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, false)
            || IsCastDefined(from, _ => to, m => m.ReturnType, true);
    }
    
    //little irrelevant DRY method
    static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType, Func<MethodInfo, Type> derivedType, 
                              bool lookInBase)
    {
        var bindinFlags = BindingFlags.Public
                        | BindingFlags.Static
                        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly);
        return type.GetMethods(bindinFlags).Any(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") 
                                                  && baseType(m).IsAssignableFrom(derivedType(m)));
    }
    
    0 讨论(0)
  • 2020-12-07 01:25

    Simplest way is value.GetType().IsSubclassOf(typeof(Control)) Basically, Type.IsSubclassOf method do what you need

    0 讨论(0)
提交回复
热议问题