Подтвердить что ты не робот

Проверьте, являются ли типы литыми/подклассами

У меня есть тип двух членов в виде строк, а не как экземпляр Type. Как проверить, защищены ли эти два типа? Скажем, строка string - "System.Windows.Forms.Label", а другая - "System.Windows.Forms.Control". Как я могу проверить, является ли первый из них подклассом (или имплицитным заклинанием) второго? Возможно ли это с помощью отражения?

Спасибо за поддержку!

4b9b3361

Ответ 1

Может показаться, что вы должны использовать Type.IsAssignableFrom но внимательно обратите внимание на документацию:

public virtual bool IsAssignableFrom(Type c)

true, если c и текущий [экземпляр] Type представляют один и тот же тип, или если ток [экземпляр] Type находится в иерархии наследования c, или если ток [экземпляр] Type представляет собой интерфейс, c орудия, или если c является параметром универсального типа и текущий [экземпляр] Type представляет одно из ограничений c. false, если ни одно из этих условий не является true, или если c является null ссылкой (Nothing в Visual Basic).

Особенно:

class Base { }
clase NotABase { public static implicit operator Base(NotABase o) { // } }

Console.WriteLine(typeof(Base).IsAssignableFrom(typeof(NotABase)));

напечатает False на консоли, даже если NotABase неявно преобразуется в Base s. Итак, для обработки приведения мы могли бы использовать отражение следующим образом:

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

Использование:

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

И для вашего случая

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

Ответ 2

Мне помогло это обсуждение, спасибо.

Я модифицировал код 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)));
}

Ответ 3

Если вы можете преобразовать эти строки в объекты Type, тогда ваш лучший выбор - Type.IsAssignableFrom.

Остерегайтесь, однако, это говорит только о том, совместимы ли два экземпляра Type на уровне CLR. Это не учитывает такие вещи, как пользовательские преобразования или другие семантики С#.

Ответ 4

Как насчет:

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

Ответ 5

Это то же самое, что и ответ Джейсона, но решает некоторые проблемы с его решением.

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

Здесь неявное отношение между Mammal и Car, но поскольку Cow равно Mammal, существует неявное преобразование от Cow до Car. Но все Car Vehicle s; поэтому a Cow перейдет в Vehicle.

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

Итак,

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

выводит true, даже если между Cow и Vehicle не существует никакого прямого преобразования.

Решение выше не подходит для примитивных типов, где преобразование встроено непосредственно в язык, чем фреймворк, поэтому что-то вроде

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

не удается. Afaik, только ручная обработка поможет. Вы получите полный список неявный и явное преобразование для числовых типы и другие примитивные типы из msdn.

Edit:

Функция 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)));
}

Ответ 6

Простейшим способом является value.GetType(). IsSubclassOf (typeof (Control)) В принципе, метод Type.IsSubclassOf делает то, что вам нужно