2010-02-08 5 views
21

Dato il tipo ae il tipo b, come posso, in fase di esecuzione, determinare se esiste una conversione implicita da a a b?Come capire se il tipo A è implicitamente convertibile in tipo B

Se questo non ha senso, si consideri il seguente metodo:

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

Ed ecco il codice chiamante che voglio lavorare:

// 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"); 

risposta

24

Nota che IsAssignableFrom non risolve il problema . Devi usare Reflection in questo modo. Nota la necessità esplicita di gestire i tipi primitivi; queste liste sono per §6.1.2 (Conversioni numeriche implicite) delle specifiche.

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

Usage:

bool b = typeof(A).IsCastableTo(typeof(B)); 
+0

Questo ci dirà che ha un metodo di conversione implicito o esplicito. Non se può essere convertito implicitamente tra tipi specifici. –

+0

Va bene, Sam, questo funzionerà per il mio problema. Sono un po 'sorpreso che non ci sia un modo integrato per farlo. –

+2

Perché non utilizzare l'enumerazione Qualsiasi estensione? –

5

conversioni implicite che avrete bisogno di prendere in considerazione:

  • Identità
  • sbyte a breve, int, long, float, double, o decimale
  • byte to short, ushort, int, uint, long, ulong, float, double o decimal
  • breve a int, long, float, double o decimale
  • ushort a int, uint, lungo, ulong, galleggiante, doppio, o decimale
  • int long, float, double o decimale
  • uint lungo, ulong, galleggiante, doppio, o decimale
  • lungo galleggiare, doppio, o decimale
  • ulong a galleggiare, doppio, o decimale
  • char a ushort, int, uint, lungo, ulong, galleggiante, double o decimal
  • float to double
  • tipo Nullable conversione di tipo
  • riferimento all'oggetto
  • classe derivata classe base
  • classe di interfaccia implementata
  • Interfaccia di interfaccia di base
  • Array a matrice quando array hanno lo stesso numero di dimensioni, ci è una conversione implicita dal tipo di elemento di origine al tipo di elemento di destinazione e il tipo di elemento di origine e il tipo di elemento di destinazione sono tipi di riferimento
  • Tipo di matrice a System.Array
  • Tipo
  • Array per IList <> e le sue interfacce di base Tipo
  • delegato System.Delegate
  • inscatolamento conversione
  • tipo Enum System.Enum
  • User conversione definito (op_implicit)

Presumo che tu stia cercando il secondo. Avrai bisogno di scrivere qualcosa che assomigli a un compilatore per coprirli tutti.Notevole è che System.Linq.Expressions.Expression non ha tentato questa impresa.

+0

Heh. Interessante. Sono davvero sorpreso che non ci sia modo di dire "Questo tipo può essere convertito in questo altro tipo". –

+0

"Matrice su array quando gli array hanno la stessa lunghezza e l'elemento ha una conversione implicita" Sei sicuro? Io non la penso così In realtà, non penso che ci sia una conversione esplicita. Per il resto, penso che il mio metodo li copra tutti. Quindi, devo fraintendere cosa intendi con "dovrai scrivere qualcosa che assomigli a un compilatore per coprirli tutti". – jason

+0

Sì, ne sono sicuro. Derivato [] è implicitamente convertibile in Base []. –

3

La risposta accettata a questa domanda gestisce molti casi, ma non tutti. Ad esempio, qui ci sono solo alcuni validi colate/conversioni che non vengono gestiti correttamente:

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

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

Qui di seguito, ho postato una versione alternativa di questa funzione che risponde specificamente la questione delle cast impliciti e conversioni. Per ulteriori dettagli, la suite di test che ho usato per verificarlo e la versione di ESPLICIT cast, controlla 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)); 
}