Ale*_*hon 3 .net c# reflection
在 .net (C#) 中,如果您通过反射发现了两种类型,是否可以确定一种是否可以转换为另一种?(隐式和/或显式)。
我想要做的是创建一个库,允许用户指定将一种类型的属性映射到另一种类型的属性。如果这两个属性具有匹配的类型,一切都很好,但我希望能够允许它们映射可以使用隐式/显式转换的属性。所以如果他们有
class from
{
public int IntProp{get;set;}
}
class to
{
public long LongProp{get;set;}
public DateTime DateTimeProp{get;set;}
}
Run Code Online (Sandbox Code Playgroud)
他们可以说 from.IntProp 将被分配给 to.LongProp (因为存在隐式转换)。但是,如果他们说它映射到 DateTimeProp,我将能够确定没有可用的强制转换并抛出异常。
小智 5
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这应该可以解决隐式和显式转换(包括数字类型、类等)
这是一个不太漂亮的实现,但我相信它涵盖了所有情况(隐式/显式运算符、可空装箱/拆箱、原始类型转换、标准转换)。请注意,说转换可能会成功与转换将会成功(这几乎不可能确定)之间是有区别的。有关更多详细信息、全面的单元测试和隐式版本,请在此处查看我的帖子。
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));
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6823 次 |
最近记录: |