检查类型是否为可转换/子类

时间:2010-01-22 18:15:39

标签: c#

我有两个成员类型作为字符串 - 而不是Type实例。如何检查这两种类型是否可浇铸?假设字符串1是“System.Windows.Forms.Label”,另一个是“System.Windows.Forms.Control”。如何检查第一个是否是第二个的子类(或隐式可转换)?这可以通过反射来实现吗?

感谢您的支持!

6 个答案:

答案 0 :(得分:16)

您似乎应该使用Type.IsAssignableFrom,但请仔细注意文档:

  

public virtual bool IsAssignableFrom(Type c)

     

true如果c和[{1}}的当前[实例]表示相同类型,或者当前[{1}}的实例位于继承层次结构中Type,或者Type的当前[实例]是c实现的接口,或者Type是泛型类型参数和当前[实例] { {1}}表示c的约束之一。 c如果这些条件都不是Type,或者cfalse引用(在Visual Basic中为true)。

特别是:

c
即使null可隐式转换为Nothing

也会在控制台上打印class Base { } clase NotABase { public static implicit operator Base(NotABase o) { // } } Console.WriteLine(typeof(Base).IsAssignableFrom(typeof(NotABase))); 。因此,为了处理强制转换,我们可以像这样使用反射:

False

用法:

NotABase

对于你的情况

Base

答案 1 :(得分:7)

我在讨论中得到了帮助,谢谢。

我修改了nawfal的代码来解决有关原始类型的问题。

现在它返回正确的结果。

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

代码如下。

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

答案 2 :(得分:6)

如果您可以将这些字符串转换为Type个对象,那么最好的选择是Type.IsAssignableFrom

请注意,这只会告诉您两个Type实例是否在CLR级别兼容。这不会考虑用户定义的转换或其他C#语义之类的内容。

答案 3 :(得分:4)

怎么样:

public bool IsCastable(String type0, String type1)
{
  return Type.GetType(type1).IsAssignableFrom(Type.GetType(type0));
}

答案 4 :(得分:2)

这与Jason的答案相同,但解决了他的解决方案的一些问题。

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

这应该处理由于继承而产生的情况。例如:

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

此处隐式关系介于MammalCar之间,但由于Cow也是Mammal,因此存在隐式转换CowCar。但所有Car都是Vehicle s;因此Cow会进入Vehicle

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

所以

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

打印为true,即使CowVehicle之间不存在直接转换运算符。

上面的解决方案对于原始类型失败,其中转换直接构建到语言而不是框架,所以类似

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

失败。 Afaik,只有手动处理才有帮助。您将获得数字类型的implicitexplicit转化以及msdn的other primitive types完整列表。

修改

IsCastableTo函数可能多一点“DRY”,或许代价是可读性较差,但我喜欢它:)

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

答案 5 :(得分:1)

最简单的方法是value.GetType()。IsSubclassOf(typeof(Control)) 基本上,Type.IsSubclassOf方法可以满足你的需要