2008-11-15 2 views
3

В .net (C#). Если у вас есть два типа, обнаруженных через отражение, можно ли определить, можно ли их перевести в другое? (неявный и/или явный).Определить, может ли отраженный тип быть преобразован в другой отраженный тип.

Что я пытаюсь сделать, это создать библиотеку, которая позволяет пользователям указывать, что свойство одного типа сопоставляется с свойством другого типа. Все хорошо, если два свойства имеют соответствующие типы, но я бы хотел, чтобы они могли отображать свойства, в которых доступен неявный/явный листинг. Так что, если у них есть

class from 
{ 
    public int IntProp{get;set;} 
} 

class to 
{ 
    public long LongProp{get;set;} 
    public DateTime DateTimeProp{get;set;} 
} 

они могли бы сказать, что from.IntProp будет назначен to.LongProp (как существует такое неявно литая). Но если бы они сказали, что он сопоставлен с DateTimeProp, я бы смог определить, что нет доступных бросков и выбрасывается исключение.

ответ

1

Было бы лучше посмотреть на TypeConverter.

0

Итак, возможно, вы имеете в виду утиную печать или структурную типизацию? Существует несколько реализаций, которые будут динамически генерировать требуемые прокси.

Например:

http://www.deftflux.net/blog/page/Duck-Typing-Project.aspx

1

Чтобы напрямую ответить на ваш вопрос ...

Если два типа обнаружены с помощью отражения можно определить, если один может быть приведен к Другие? (Неявный и/или неявное)

... вы можете использовать что-то похожее на это:

to.GetType().IsAssignableFrom(from.GetType()); 

Метод Type.IsAssignableFrom() может быть использован именно для ваших целей. Это также было бы значительно менее подробным (хотя бы незначительно более эффективным), чем использование TypeConverters.

+9

Согласно MSDN, IsAssignableFrom рассматривает только равенство, наследование, интерфейсы и дженерики, а не литые операторы. http://msdn.microsoft.com/en-us/library/system.type.isassignablefrom.aspx – 2010-07-01 19:27:09

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; 
      } 
     } 

Это должно сделать трюк для неявных и явных преобразований (в том числе числовых типов, классов и т.д.)

+0

Очень приятное использование выражений. Наверняка это ответ? – bart 2013-02-21 00:40:25

2

Вот реализация не очень, но которые я считаю, охватывает все случаи (неявных/явные операторы , обнуляемый бокс/распаковка, конверсии примитивного типа, стандартные отбрасывания). Обратите внимание, что есть разница между тем, что конверсия МОЖЕТ преуспеть в сравнении с тем, что она будет успешной (что почти невозможно узнать наверняка). Для получения более подробной информации, всеобъемлющего модульного теста и неявной версии, проверьте мой пост here.

public static bool IsCastableTo(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"); 

    // 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)); 
} 
Смежные вопросы