代码之家  ›  专栏  ›  技术社区  ›  Judah Gabriel Himango

如何判断类型A是否隐式转换为类型B

  •  22
  • Judah Gabriel Himango  · 技术社区  · 14 年前

    给定类型A和类型B,如何在运行时确定是否存在从A到B的隐式转换?

    如果这不合理,请考虑以下方法:

    public PropertyInfo GetCompatibleProperty<T>(object instance, string propertyName)
    {
       var property = instance.GetType().GetProperty(propertyName);
    
       bool isCompatibleProperty = !property.PropertyType.IsAssignableFrom(typeof(T));
       if (!isCompatibleProperty) throw new Exception("OH NOES!!!");
    
       return property;   
    }
    

    下面是我要使用的呼叫代码:

    // Since string.Length is an int property, and ints are convertible
    // to double, this should work, but it doesn't. :-(
    var property = GetCompatibleProperty<double>("someStringHere", "Length");
    
    3 回复  |  直到 6 年前
        1
  •  25
  •   Chris Hynes    10 年前

    注意 IsAssignableFrom 不能解决你的问题。你必须像这样运用反省。注意显式地需要处理基元类型;这些列表是根据规范的_6.1.2(隐式数字转换)。

    static class TypeExtensions { 
        static Dictionary<Type, List<Type>> dict = new Dictionary<Type, List<Type>>() {
            { typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
            { typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
            { typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
            { typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
            { typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
            { typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } },
            { typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
            { typeof(ushort), new List<Type> { typeof(byte), typeof(char) } },
            { typeof(short), new List<Type> { typeof(byte) } }
        };
        public static bool IsCastableTo(this Type from, Type to) { 
            if (to.IsAssignableFrom(from)) { 
                return true; 
            }
            if (dict.ContainsKey(to) && dict[to].Contains(from)) {
                return true;
            }
            bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static) 
                            .Any( 
                                m => m.ReturnType == to &&  
                                (m.Name == "op_Implicit" ||  
                                m.Name == "op_Explicit")
                            ); 
            return castable; 
        } 
    } 
    

    用途:

    bool b = typeof(A).IsCastableTo(typeof(B));
    
        2
  •  5
  •   jason    14 年前

    您需要考虑的隐式转换:

    • 身份
    • 从sbyte到short、int、long、float、double或decimal
    • 字节到短、ushort、int、uint、long、ulong、float、double或decimal
    • 短到int、long、float、double或decimal
    • ushort到int、uint、long、ulong、float、double或decimal
    • int到long、float、double或decimal
    • uint到long、ulong、float、double或decimal
    • 长到浮点、双精度或十进制
    • ulong到float、double或decimal
    • char到ushort、int、uint、long、ulong、float、double或decimal
    • 双浮动
    • 可为空的类型转换
    • 对象的引用类型
    • 派生类到基类
    • 类到实现的接口
    • 接口到基本接口
    • 数组到数组当数组的维数相同时,存在从源元素类型到目标元素类型的隐式转换,源元素类型和目标元素类型是引用类型
    • 数组类型到System.Array
    • IList的数组类型及其基本接口
    • 将类型委托给System.Delegate
    • 装箱转换
    • 枚举类型到System.Enum
    • 用户定义的转换(op_隐式)

    我想你在找后者。您需要编写类似于编译器的东西来覆盖所有这些内容。值得注意的是,system.linq.expressions.expression没有尝试此功能。

        3
  •  5
  •   ChaseMedallion    6 年前

    这个问题的公认答案可以处理许多情况,但并非全部。例如,以下只是一些未正确处理的有效强制转换/转换:

    // explicit
    var a = (byte)2;
    var b = (decimal?)2M;
    
    // implicit
    double? c = (byte)2;
    decimal? d = 4L;
    

    下面,我发布了这个函数的另一个版本,专门解答了隐式强制转换和转换的问题。有关详细信息,请查看我用来验证它的测试套件和显式强制转换版本。 my post on the subject .

    public static bool IsImplicitlyCastableTo(this Type from, Type to)
    {
        // from http://www.codeducky.org/10-utilities-c-developers-should-know-part-one/ 
        Throw.IfNull(from, "from");
        Throw.IfNull(to, "to");
    
        // not strictly necessary, but speeds things up
        if (to.IsAssignableFrom(from))
        {
            return true;
        }
    
        try
        {
            // overload of GetMethod() from http://www.codeducky.org/10-utilities-c-developers-should-know-part-two/ 
            // that takes Expression<Action>
            ReflectionHelpers.GetMethod(() => AttemptImplicitCast<object, object>())
                .GetGenericMethodDefinition()
                .MakeGenericMethod(from, to)
                .Invoke(null, new object[0]);
            return true;
        }
        catch (TargetInvocationException ex)
        {
            return = !(
                ex.InnerException is RuntimeBinderException
                // if the code runs in an environment where this message is localized, we could attempt a known failure first and base the regex on it's message
                && Regex.IsMatch(ex.InnerException.Message, @"^The best overloaded method match for 'System.Collections.Generic.List<.*>.Add(.*)' has some invalid arguments$")
            );
        }
    }
    
    private static void AttemptImplicitCast<TFrom, TTo>()
    {
        // based on the IL produced by:
        // dynamic list = new List<TTo>();
        // list.Add(default(TFrom));
        // We can't use the above code because it will mimic a cast in a generic method
        // which doesn't have the same semantics as a cast in a non-generic method
    
        var list = new List<TTo>(capacity: 1);
        var binder = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
            flags: CSharpBinderFlags.ResultDiscarded, 
            name: "Add", 
            typeArguments: null, 
            context: typeof(TypeHelpers), // the current type
            argumentInfo: new[] 
            { 
                CSharpArgumentInfo.Create(flags: CSharpArgumentInfoFlags.None, name: null), 
                CSharpArgumentInfo.Create(
                    flags: CSharpArgumentInfoFlags.UseCompileTimeType, 
                    name: null
                ),
            }
        );
        var callSite = CallSite<Action<CallSite, object, TFrom>>.Create(binder);
        callSite.Target.Invoke(callSite, list, default(TFrom));
    }