2010-01-22 12 views
15

Tengo el tipo de dos miembros como cadenas, y no como una instancia de tipo. ¿Cómo puedo verificar si los dos tipos son moldeables? Digamos que la primera cadena es "System.Windows.Forms.Label" y la otra es "System.Windows.Forms.Control". ¿Cómo puedo verificar si la primera es una subclase (o moldeable implícita) de la segunda? ¿Es esto posible usando la reflexión?Compruebe si los tipos son moldeables/subclases

Gracias por su apoyo!

+1

¿De dónde vienen estas cadenas? ¿Qué está tratando de lograr? – n8wrl

Respuesta

15

Podría parecer que se debe utilizar Type.IsAssignableFrom pero tenga en cuenta cuidadosamente la documentación:

public virtual bool IsAssignableFrom(Type c)

true si c y la corriente [instancia de] Type representan el mismo tipo, o si la corriente [ instancia de] Type se encuentra en la jerarquía de herencia de c, o si la [instancia actual] de Type es una interfaz que implementa c, o si c es un tipo genérico paramet er y la instancia actual [instancia de] Type representa una de las restricciones de c. false si ninguna de estas condiciones es true, o si c es una referencia null (Nothing en Visual Basic).

En particular:

class Base { } 
clase NotABase { public static implicit operator Base(NotABase o) { // } } 

Console.WriteLine(typeof(Base).IsAssignableFrom(typeof(NotABase))); 

imprimirá False en la consola aunque NotABase s son implícitamente moldeable a Base s. Por lo tanto, para manejar la fundición, podríamos utilizar la reflexión de este modo:

static class TypeExtensions { 
    public static bool IsCastableTo(this Type from, Type to) { 
     if (to.IsAssignableFrom(from)) { 
      return true; 
     } 
     var methods = from.GetMethods(BindingFlags.Public | BindingFlags.Static) 
          .Where(
           m => m.ReturnType == to && 
            (m.Name == "op_Implicit" || 
            m.Name == "op_Explicit") 
         ); 
     return methods.Any(); 
    } 
} 

Uso:

Console.WriteLine(typeof(string).IsCastableTo(typeof(int))); // false 
Console.WriteLine(typeof(NotABase).IsCastableTo(typeof(Base))); // true 

Y para su caso

// from is string representing type name, e.g. "System.Windows.Forms.Label" 
// to is string representing type name, e.g. "System.Windows.Forms.Control" 
Type fromType = Type.GetType(from); 
Type toType = Type.GetType(to); 
bool castable = from.IsCastableTo(to); 
+4

'typeof (short) .IsCastableTo (typeof (int))' devuelve 'false', aunque' short' es implícitamente convertible a 'int'. ¿Hay alguna manera de hacer que tu método funcione también para ese caso? –

+1

Gran solución. Para completar, creo que debería verificar m.ReturnType.IsAssignable (to) en lugar de verificar la igualdad. Esto significa que obtiene operadores implícitos que devuelven más tipos derivados. Un caso raro pero vale la pena arrojarlo allí. – Gusdor

+1

@Jason, hay un pequeño problema aquí, donde debe realizar un bucle a través de ambos tipos, ya que los operadores de conversión se pueden definir en cualquier tipo. También como Gusdor dice 'IsAssignableFrom' se haría cargo de los moldes posibles a través de la herencia. Sin embargo, es un excelente comienzo. Intentaré expandirlo. – nawfal

6

Si usted puede convertir estas cadenas para Type objetos continuación, su mejor apuesta es Type.IsAssignableFrom.

Sin embargo, tenga en cuenta que dos instancias Type son compatibles en un nivel CLR. Esto no tendrá en cuenta cosas como las conversiones definidas por el usuario u otras semánticas de C#.

4

¿Qué tal:

public bool IsCastable(String type0, String type1) 
{ 
    return Type.GetType(type1).IsAssignableFrom(Type.GetType(type0)); 
} 
+1

Es importante tener en cuenta que esto en realidad no verifica si el tipo representado por 'type0' es moldeable al tipo representado por' type1'. Su método imprimirá 'false' para la clase' Base {} clase NotABase {public static implicit operator Base (NotABase o) {// details}} 'aunque un' NotABase' se puede convertir en 'Base'.Como la pregunta me pide que maneje el caso en el que un lanzamiento implícito es posible, estoy bajando votos. – jason

0

forma más sencilla es value.GetType() IsSubclassOf (typeof (control)) Básicamente, el método Type.IsSubclassOf hacer lo que necesita

1

Esto es igual que Jason. responde, pero resuelve algunos problemas con su solución.

public static bool IsCastableTo(this Type from, Type to) 
{ 
    return to.IsAssignableFrom(from) 
     || to.GetConvertOperators().Any(m => m.GetParameters()[0].ParameterType.IsAssignableFrom(from)) 
     || from.GetConvertOperators(true).Any(m => to.IsAssignableFrom(m.ReturnType)); 
} 

public static IEnumerable<MethodInfo> GetConvertOperators(this Type type, bool lookInBase = false) 
{ 
    var bindinFlags = BindingFlags.Public 
        | BindingFlags.Static 
        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); 
    return type.GetMethods(bindinFlags).Where(m => m.Name == "op_Implicit" || m.Name == "op_Explicit"); 
} 

Esto debe manejar las situaciones que surgen debido a la herencia también.Por ejemplo:

class Mammal { public static implicit operator Car (Mammal o) { return null; } } 
class Cow : Mammal { } 
class Vehicle { } 
class Car : Vehicle { } 

Aquí la relación implícita es entre Mammal y Car, pero desde Cow es Mammal así, existen una conversión implícita de Cow a Car. Pero todos Car s son Vehicle s; por lo tanto, un Cow entraría en un Vehicle.

Cow c = null; 
Vehicle v = c; //legal 

Así

typeof(Cow).IsCastableTo(typeof(Vehicle)); //true 

impresiones de verdad, a pesar de que ningún operador de conversión directa existen entre Cow y Vehicle.

La solución anterior falla para los tipos primitivos, donde la conversión se construye directamente en el idioma que el marco, así que algo como

typeof(short).IsCastableTo(typeof(int)); 

falla. Afaik, solo el manejo manual lo ayudará. Obtendrá la lista completa de implicit y explicit conversión para los tipos numéricos y other primitive types de msdn.

Editar:

La función IsCastableTo podría ser poco más "DRY", tal vez a costa de ser menos legibles, pero me gusta :)

public static bool IsCastableTo(this Type from, Type to) 
{ 
    return to.IsAssignableFrom(from) 
     || IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, false) 
     || IsCastDefined(from, _ => to, m => m.ReturnType, true); 
} 

//little irrelevant DRY method 
static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType, Func<MethodInfo, Type> derivedType, 
          bool lookInBase) 
{ 
    var bindinFlags = BindingFlags.Public 
        | BindingFlags.Static 
        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); 
    return type.GetMethods(bindinFlags).Any(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") 
               && baseType(m).IsAssignableFrom(derivedType(m))); 
} 
+0

¿qué quieres decir con seco? –

+0

Además, todavía no resuelve el problema con int <==> corto o largo –

+0

@LouisRhys dry points a http://en.wikipedia.org/wiki/Don't_repeat_yourself que he actualizado en la respuesta. En mi primera respuesta, estoy usando la funcionalidad 'IsAssignableFrom' dos veces haciendo más o menos lo mismo. En el segundo enfoque refactoré eso a un único método. Se pone un poco torpe allí, pero me gusta más. – nawfal

5

que fue ayudado por este discusión, gracias.

Modifiqué el código de nawfal para resolver el problema sobre los tipos primitivos.

Ahora devuelve resultados correctos.

typeof(short).IsCastableTo(typeof(int)); // True 
typeof(short).IsCastableTo(typeof(int), implicitly:true); // True 
typeof(int).IsCastableTo(typeof(short)); // True 
typeof(int).IsCastableTo(typeof(short), implicitly:true); // False 

El código es el siguiente.

public static bool IsCastableTo(this Type from, Type to, bool implicitly = false) 
{ 
    return to.IsAssignableFrom(from) || from.HasCastDefined(to, implicitly); 
} 

static bool HasCastDefined(this Type from, Type to, bool implicitly) 
{ 
    if ((from.IsPrimitive || from.IsEnum) && (to.IsPrimitive || to.IsEnum)) 
    { 
     if (!implicitly) 
      return from==to || (from!=typeof(Boolean) && to!=typeof(Boolean)); 

     Type[][] typeHierarchy = { 
      new Type[] { typeof(Byte), typeof(SByte), typeof(Char) }, 
      new Type[] { typeof(Int16), typeof(UInt16) }, 
      new Type[] { typeof(Int32), typeof(UInt32) }, 
      new Type[] { typeof(Int64), typeof(UInt64) }, 
      new Type[] { typeof(Single) }, 
      new Type[] { typeof(Double) } 
     }; 
     IEnumerable<Type> lowerTypes = Enumerable.Empty<Type>(); 
     foreach (Type[] types in typeHierarchy) 
     { 
      if (types.Any(t => t == to)) 
       return lowerTypes.Any(t => t == from); 
      lowerTypes = lowerTypes.Concat(types); 
     } 

     return false; // IntPtr, UIntPtr, Enum, Boolean 
    } 
    return IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, implicitly, false) 
     || IsCastDefined(from, _ => to, m => m.ReturnType, implicitly, true); 
} 

static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType, 
         Func<MethodInfo, Type> derivedType, bool implicitly, bool lookInBase) 
{ 
    var bindinFlags = BindingFlags.Public | BindingFlags.Static 
        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); 
    return type.GetMethods(bindinFlags).Any(
     m => (m.Name=="op_Implicit" || (!implicitly && m.Name=="op_Explicit")) 
      && baseType(m).IsAssignableFrom(derivedType(m))); 
} 
+0

edite su respuesta si quiero cambiar las cosas – flx

Cuestiones relacionadas