给定类型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");
答案 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)
您需要考虑的隐含转换:
我认为你正在寻找后者。你需要编写类似于编译器的东西来覆盖所有这些东西。值得注意的是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