在.net(C#)中,如果您通过反射发现了两种类型,是否可以确定是否可以将其转换为另一种? (隐式和/或显式)。
我要做的是创建一个库,允许用户指定一种类型的属性映射到另一种类型的属性。如果两个属性具有匹配类型,一切都很好,但我希望能够允许它们映射隐式/显式转换可用的属性。所以,如果他们有
class from
{
public int IntProp{get;set;}
}
class to
{
public long LongProp{get;set;}
public DateTime DateTimeProp{get;set;}
}
他们可以说from.IntProp将被分配到.LongProp(作为一个隐含的演员存在)。但如果他们说它映射到DateTimeProp我就能确定没有可用的强制转换并抛出异常。
答案 0 :(得分:4)
public static bool HasConversionOperator( Type from, Type to )
{
Func<Expression, UnaryExpression> bodyFunction = body => Expression.Convert( body, to );
ParameterExpression inp = Expression.Parameter( from, "inp" );
try
{
// If this succeeds then we can cast 'from' type to 'to' type using implicit coercion
Expression.Lambda( bodyFunction( inp ), inp ).Compile();
return true;
}
catch( InvalidOperationException )
{
return false;
}
}
这应该是隐式和显式转换(包括数字类型,类等)的技巧
答案 1 :(得分:2)
这里的实现并不漂亮,但我认为它涵盖了所有情况(隐式/显式运算符,可空装箱/拆箱,基本类型转换,标准强制转换)。请注意,说转换可能成功与成功之间存在差异(这几乎不可能确定)。有关详细信息,全面的单元测试和隐式版本,请查看我的帖子here。
public static bool IsCastableTo(this Type from, Type to)
{
// from https://web.archive.org/web/20141017005721/http://www.codeducky.org/10-utilities-c-developers-should-know-part-one/
Throw.IfNull(from, "from");
Throw.IfNull(to, "to");
// explicit conversion always works if to : from OR if
// there's an implicit conversion
if (from.IsAssignableFrom(to) || from.IsImplicitlyCastableTo(to))
{
return true;
}
// for nullable types, we can simply strip off the nullability and evaluate the underyling types
var underlyingFrom = Nullable.GetUnderlyingType(from);
var underlyingTo = Nullable.GetUnderlyingType(to);
if (underlyingFrom != null || underlyingTo != null)
{
return (underlyingFrom ?? from).IsCastableTo(underlyingTo ?? to);
}
if (from.IsValueType)
{
try
{
ReflectionHelpers.GetMethod(() => AttemptExplicitCast<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, @"^Cannot convert type '.*' to '.*'$")
);
}
}
else
{
// if the from type is null, the dynamic logic above won't be of any help because
// either both types are nullable and thus a runtime cast of null => null will
// succeed OR we get a runtime failure related to the inability to cast null to
// the desired type, which may or may not indicate an actual issue. thus, we do
// the work manually
return from.IsNonValueTypeExplicitlyCastableTo(to);
}
}
private static bool IsNonValueTypeExplicitlyCastableTo(this Type from, Type to)
{
if ((to.IsInterface && !from.IsSealed)
|| (from.IsInterface && !to.IsSealed))
{
// any non-sealed type can be cast to any interface since the runtime type MIGHT implement
// that interface. The reverse is also true; we can cast to any non-sealed type from any interface
// since the runtime type that implements the interface might be a derived type of to.
return true;
}
// arrays are complex because of array covariance
// (see http://msmvps.com/blogs/jon_skeet/archive/2013/06/22/array-covariance-not-just-ugly-but-slow-too.aspx).
// Thus, we have to allow for things like var x = (IEnumerable<string>)new object[0];
// and var x = (object[])default(IEnumerable<string>);
var arrayType = from.IsArray && !from.GetElementType().IsValueType ? from
: to.IsArray && !to.GetElementType().IsValueType ? to
: null;
if (arrayType != null)
{
var genericInterfaceType = from.IsInterface && from.IsGenericType ? from
: to.IsInterface && to.IsGenericType ? to
: null;
if (genericInterfaceType != null)
{
return arrayType.GetInterfaces()
.Any(i => i.IsGenericType
&& i.GetGenericTypeDefinition() == genericInterfaceType.GetGenericTypeDefinition()
&& i.GetGenericArguments().Zip(to.GetGenericArguments(), (ia, ta) => ta.IsAssignableFrom(ia) || ia.IsAssignableFrom(ta)).All(b => b));
}
}
// look for conversion operators. Even though we already checked for implicit conversions, we have to look
// for operators of both types because, for example, if a class defines an implicit conversion to int then it can be explicitly
// cast to uint
const BindingFlags conversionFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
var conversionMethods = from.GetMethods(conversionFlags)
.Concat(to.GetMethods(conversionFlags))
.Where(m => (m.Name == "op_Explicit" || m.Name == "op_Implicit")
&& m.Attributes.HasFlag(MethodAttributes.SpecialName)
&& m.GetParameters().Length == 1
&& (
// the from argument of the conversion function can be an indirect match to from in
// either direction. For example, if we have A : B and Foo defines a conversion from B => Foo,
// then C# allows A to be cast to Foo
m.GetParameters()[0].ParameterType.IsAssignableFrom(from)
|| from.IsAssignableFrom(m.GetParameters()[0].ParameterType)
)
);
if (to.IsPrimitive && typeof(IConvertible).IsAssignableFrom(to))
{
// as mentioned above, primitive convertible types (i. e. not IntPtr) get special
// treatment in the sense that if you can convert from Foo => int, you can convert
// from Foo => double as well
return conversionMethods.Any(m => m.ReturnType.IsCastableTo(to));
}
return conversionMethods.Any(m => m.ReturnType == to);
}
private static void AttemptExplicitCast<TFrom, TTo>()
{
// based on the IL generated from
// var x = (TTo)(dynamic)default(TFrom);
var binder = Microsoft.CSharp.RuntimeBinder.Binder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(TTo), typeof(TypeHelpers));
var callSite = CallSite<Func<CallSite, TFrom, TTo>>.Create(binder);
callSite.Target(callSite, default(TFrom));
}
答案 2 :(得分:1)
最好查看TypeConverter的内容。
答案 3 :(得分:1)
直接回答你的问题...
如果您通过反射发现了两种类型,则可以确定是否可以将一种类型转换为另一种类型? (隐式和/或显式)
......你可以使用类似的东西:
to.GetType().IsAssignableFrom(from.GetType());
Type.IsAssignableFrom()方法可用于您的目的。与使用TypeConverters相比,这也会相当简单(即使只是稍微提高性能)。
答案 4 :(得分:0)
那么,可能你的意思是鸭子打字或结构打字?有几种实现可以动态生成所需的代理。
例如: