Проверьте, являются ли типы литыми/подклассами
У меня есть тип двух членов в виде строк, а не как экземпляр Type. Как проверить, защищены ли эти два типа? Скажем, строка string - "System.Windows.Forms.Label", а другая - "System.Windows.Forms.Control". Как я могу проверить, является ли первый из них подклассом (или имплицитным заклинанием) второго? Возможно ли это с помощью отражения?
Спасибо за поддержку!
Ответы
Ответ 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 делает то, что вам нужно