如何确定C#中是否存在隐式转换?

时间:2015-08-15 13:28:24

标签: c# .net reflection casting implicit-conversion

我有两种类型,T和U,我想知道是否从T到U定义了隐式强制转换运算符。

我知道IsAssignableFrom的存在,这不是我想要的,因为它不涉及隐式演员。

一些谷歌搜索引导我this solution,但用作者自己的话说这是丑陋的代码(它试图隐式转换,如果有异常则返回false,否则为真......)

似乎正在测试是否存在具有正确签名won't work for primitive types的op_Implicit方法。

是否有更简洁的方法来确定隐式强制转换运算符的存在?

1 个答案:

答案 0 :(得分:5)

我最终手动处理了原始类型场景。不是很优雅,但它有效。

我还添加了额外的逻辑来处理可空类型和枚举。

我重复使用Poke's code作为用户定义的类型场景。

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

    }
}