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

时间:2010-02-08 19:30:24

标签: .net reflection type-conversion implicit-conversion

给定类型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");

4 个答案:

答案 0 :(得分:25)

请注意,IsAssignableFrom无法解决您的问题。你必须像这样使用Reflection。注意显式需要处理基元类型;这些列表符合规范的§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));

答案 1 :(得分:5)

您需要考虑的隐含转换:

  • 身份
  • sbyte为short,int,long,float,double或decimal
  • byte to short,ushort,int,uint,long,ulong,float,double或decimal
  • 简称为int,long,float,double或decimal
  • ushort to int,uint,long,ulong,float,double或decimal
  • int to long,float,double或decimal
  • uint to long,ulong,float,double或decimal
  • long to float,double或decimal
  • ulong to float,double或decimal
  • char to ushort,int,uint,long,ulong,float,double或decimal
  • 浮动加倍
  • Nullable type conversion
  • 参考类型到对象
  • 派生类到基类
  • 实施接口的类
  • 基础接口的接口
  • 当数组具有相同维数时,数组到数组,存在从源元素类型到目标元素类型的隐式转换,源元素类型和目标元素类型是引用类型
  • 数组类型为System.Array
  • IList&lt;&gt;的数组类型及其基础接口
  • 将类型委托给System.Delegate
  • 拳击转换
  • 枚举类型为System.Enum
  • 用户定义的转换(op_implicit)

我认为你正在寻找后者。你需要编写类似于编译器的东西来覆盖所有这些东西。值得注意的是System.Linq.Expressions.Expression没有尝试这一壮举。

答案 2 :(得分:5)

这个问题的接受答案处理了很多案件,但不是全部。例如,以下是一些未正确处理的有效演员表/转换:

// explicit
var a = (byte)2;
var b = (decimal?)2M;

// implicit
double? c = (byte)2;
decimal? d = 4L;

下面,我发布了此功能的替代版本,专门回答了IMPLICIT演员和转换的问题。有关详细信息,我用于验证的测试套件以及EXPLICIT演员版,请查看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));
}

答案 3 :(得分:0)

这是一种通过以下所有测试的方法:

@Override
 public void run() {
  try {
   Instrumentation inst = new Instrumentation();
   inst.sendKeyDownUpSync(KeyEvent.KEYCODE_VOLUME_UP);
  } catch (Exception e) {

  }

 }
}).start()

它基于[Test] public void TestImplicitlyCastable() { Assert.That( typeof(byte) .IsImplicitlyCastableTo(typeof(short))); Assert.That( typeof(byte) .IsImplicitlyCastableTo(typeof(byte?))); Assert.That( typeof(byte) .IsImplicitlyCastableTo(typeof(long?))); Assert.That(!typeof(short) .IsImplicitlyCastableTo(typeof(uint))); Assert.That( typeof(long) .IsImplicitlyCastableTo(typeof(float))); Assert.That( typeof(long) .IsImplicitlyCastableTo(typeof(decimal))); Assert.That(!typeof(double) .IsImplicitlyCastableTo(typeof(decimal))); Assert.That(!typeof(decimal) .IsImplicitlyCastableTo(typeof(double))); Assert.That( typeof(List<int>).IsImplicitlyCastableTo(typeof(object))); Assert.That( typeof(float) .IsImplicitlyCastableTo(typeof(IComparable<float>))); Assert.That( typeof(long?) .IsImplicitlyCastableTo(typeof(IComparable<long>))); Assert.That(!typeof(object) .IsImplicitlyCastableTo(typeof(string))); Assert.That( typeof(string[]).IsImplicitlyCastableTo(typeof(object[]))); Assert.That( typeof(Foo) .IsImplicitlyCastableTo(typeof(int))); Assert.That(!typeof(Foo) .IsImplicitlyCastableTo(typeof(uint))); Assert.That( typeof(Foo) .IsImplicitlyCastableTo(typeof(long))); Assert.That( typeof(Foo) .IsImplicitlyCastableTo(typeof(long?))); } class Foo { public static implicit operator int(Foo f) => 42; } 的技巧,灵感来自ChaseMedallion的答案:

dynamic