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" and the other one is "System.Windows.Forms.Control". How can I check if the first one is a subclass (or implicit castable) of the second one? Is this possible by using reflection?
Thanks for you support!
It might seem like you should use Type.IsAssignableFrom but note carefully the documentation:
public virtual bool IsAssignableFrom(Type c)
trueifcand the current [instance of]Typerepresent the same type, or if the current [instance of]Typeis in the inheritance hierarchy ofc, or if the current [instance of]Typeis an interface thatcimplements, or ifcis a generic type parameter and the current [instance of]Typerepresents one of the constraints ofc.falseif none of these conditions aretrue, or ifcis anullreference (Nothingin 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);
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)));
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With