How can I determine if an implicit cast exists in C#?

后端 未结 3 1455
予麋鹿
予麋鹿 2021-02-18 13:16

I have two types, T and U, and I want to know whether an implicit cast operator is defined from T to U.

I\'m aware of the existence of IsAssignableFrom, and this is not

相关标签:
3条回答
  • 2021-02-18 14:05

    You could use reflection to find the implicit conversion method for the target type:

    public static bool HasImplicitConversion(Type baseType, Type targetType)
    {
        return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
            .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
            .Any(mi => {
                ParameterInfo pi = mi.GetParameters().FirstOrDefault();
                return pi != null && pi.ParameterType == baseType;
            });
    }
    

    You can use it like this:

    class X {}
    class Y
    {
        public static implicit operator X (Y y)
        {
            return new X();
        }
    
        public static implicit operator Y (X x)
        {
            return new Y();
        }
    }
    
    // and then:
    bool conversionExists = HasImplicitConversion(typeof(Y), typeof(X));
    

    Note that this only checks for an implicit type conversion on the base type (the first passed type). Technically, the type conversion can also be defined on the other type, so you may need to call it again with the types reversed (or build that into the method). Implicit type conversions may not exist on both types though.

    0 讨论(0)
  • 2021-02-18 14:07

    I ended up handling the primitive types scenario manually. Not very elegant, but it works.

    I've also added additional logic to handle nullable types and enums.

    I reused Poke's code for the user-defined type scenario.

    public class AvailableCastChecker
    {
        public static bool CanCast(Type from, Type to)
        {
            if (from.IsAssignableFrom(to))
            {
                return true;
            }
            if (HasImplicitConversion(from, from, to)|| HasImplicitConversion(to, from, to))
            {
                return true;
            }
            List<Type> list;
            if (ImplicitNumericConversions.TryGetValue(from, out list))
            {
                if (list.Contains(to))
                    return true;
            }
    
            if (to.IsEnum)
            {
                return CanCast(from, Enum.GetUnderlyingType(to));
            }
            if (Nullable.GetUnderlyingType(to) != null)
            {
                return CanCast(from, Nullable.GetUnderlyingType(to));
            }
    
            return false;
        }
    
        // https://msdn.microsoft.com/en-us/library/y5b434w4.aspx
        static Dictionary<Type,List<Type>> ImplicitNumericConversions = new Dictionary<Type, List<Type>>();
    
        static AvailableCastChecker()
        {
            ImplicitNumericConversions.Add(typeof(sbyte), new List<Type> {typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(byte), new List<Type> { typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(short), new List<Type> {  typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(ushort), new List<Type> { typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(int), new List<Type> { typeof(long), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(uint), new List<Type> { typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(long), new List<Type> { typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(char), new List<Type> { typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });
            ImplicitNumericConversions.Add(typeof(float), new List<Type> { typeof(double) });
            ImplicitNumericConversions.Add(typeof(ulong), new List<Type> { typeof(float), typeof(double), typeof(decimal) });
        }
    
        static bool HasImplicitConversion(Type definedOn, Type baseType, Type targetType)
        {
            return definedOn.GetMethods(BindingFlags.Public | BindingFlags.Static)
                .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
                .Any(mi =>
                {
                    ParameterInfo pi = mi.GetParameters().FirstOrDefault();
                    return pi != null && pi.ParameterType == baseType;
                });
    
        }
    }
    
    0 讨论(0)
  • 2021-02-18 14:07

    Here's a solution I found. The major code shown as bellow (after some simple translation):

    public static bool IsImplicitFrom(this Type type, Type fromType) {
        if (type == null || fromType == null) {
            return false;
        }
    
        // support for reference type
        if (type.IsByRef) { type = type.GetElementType(); }
        if (fromType.IsByRef) { fromType = type.GetElementType(); }
    
        // could always be convert to object
        if (type.Equals(typeof(object))) {
            return true;
        }
    
        // check if it could be convert using standard implicit cast
        if (IsStandardImplicitFrom(type, fromType)) {
            return true;
        }
    
        // determine implicit convert operator
        Type nonNullalbeType, nonNullableFromType;
        if (IsNullableType(type, out nonNullalbeType) && 
            IsNullableType(fromType, out nonNullableFromType)) {
            type = nonNullalbeType;
            fromType = nonNullableFromType;
        }
    
        return ConversionCache.GetImplicitConversion(fromType, type) != null;
    }
    
    internal static bool IsStandardImplicitFrom(this Type type, Type fromType) {
        // support for Nullable<T>
        if (!type.IsValueType || IsNullableType(ref type)) {
            fromType = GetNonNullableType(fromType);
        }
    
        // determine implicit value type convert
        HashSet<TypeCode> typeSet;
        if (!type.IsEnum && 
            ImplicitNumericConversions.TryGetValue(Type.GetTypeCode(type), out typeSet)) {
            if (!fromType.IsEnum && typeSet.Contains(Type.GetTypeCode(fromType))) {
                return true;
            }
        }
    
        // determine implicit reference type convert and boxing convert
        return type.IsAssignableFrom(fromType);
    }
    

    Update: Here's the whole file.

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