2011-01-09 14 views
11

Lo mismo para también métodos:¿Cómo comparo dos PropertyInfos o métodos de manera confiable?

me dan dos ejemplos de PropertyInfo o métodos que han sido extraídos de la clase que se sientan en medio de GetProperty() o GetMember() etc, (o tal vez de una MemberExpression).

Quiero determinar si son, de hecho, refiriéndose a la misma propiedad o el mismo método por lo

(propertyOne == propertyTwo) 

o

(methodOne == methodTwo) 

Es evidente que no se va a trabajar en realidad, es posible que estar buscando la misma propiedad, pero podría haber sido extraída de diferentes niveles de la jerarquía de clases (en cuyo caso generalmente, propertyOne != propertyTwo)

De curso e, yo pude ver DeclaringType, y volver a la petición de la propiedad, pero esto empieza a tener un poco confuso cuando se empieza a pensar en

  • Propiedades/métodos declarados en las interfaces e implementados en clases
  • Propiedades/Los métodos declarados en una clase base (virtualmente) y cambiados en las clases derivadas
  • Propiedades/métodos declarados en una clase base, se reemplaza con el 'nuevo' (en la IL mundo esto no es nada IIRC especial)

al final de la día, solo quiero poder hacer una ecuación inteligente verificación de la calidad entre dos propiedades o dos métodos, estoy 80% seguro de que los puntos anteriores no cubren todos los casos extremos, y aunque podía simplemente sentarme, escribir un montón de pruebas y comenzar a jugar sobre, me ' Estoy muy consciente de que mi bajo nivel de conocimiento sobre cómo estos conceptos se implementan realmente no es excelente, y espero que este sea un tema ya respondido y me apetece buscar.

La mejor respuesta me daría un par de métodos que permitan alcanzar lo anterior, explicando qué casos extremos han sido atendidos y por qué :-)


Aclaración:

Literalmente, Quiero para asegurarse de que son la misma propiedad, he aquí algunos ejemplos

public interface IFoo 
{ 
    string Bar { get; set; } 
} 

public class Foo : IFoo 
{ 
    string Bar { get; set; } 
} 

typeof(IFoo).GetProperty("Bar") 

y

typeof(Foo).GetProperty("Bar") 

Volverá dos informaciones de propiedad, que no son iguales:

public class BaseClass 
{ 
    public string SomeProperty { get; set ; } 
} 

public class DerivedClass : BaseClass { } 


typeof(BaseClass).GetMethod("SomeProperty") 

y

typeof(DerivedClass).GetProperty("SomeProperty") 

realidad no puedo recordar si estos dos objetos de retorno igual ahora, pero en mi mundo son iguales.

mismo modo:

public class BaseClass 
{ 
    public virtual SomeMethod() { } 
} 

public class DerivedClass 
{ 
    public override SomeMethod() { } 
} 

typeof(BaseClass).GetMethod("SomeMethod") 

y

typeof(DerivedClass).GetProperty("SomeMethod") 

vez más, estos no coincidirán - pero ellos quieren (sé que no son específicamente iguales, pero en mi dominio que están porque se refieren a la misma propiedad original)

Podría hacerlo estructuralmente, pero eso sería 'incorrecto'.

Otras indicaciones:

¿Cómo se puede solicitar incluso la propiedad que se esconde otra propiedad? Parece que uno de mis supuestos anteriores no era válido, que la implementación predeterminada de GetProperty("name") se referiría al nivel actual de forma predeterminada.

BindingFlags.DeclaringType parece que acaba regresando nulo!

+1

"Propiedades/Métodos declarados en una clase base, que se modifique con 'nuevo'" - Eso se llama en realidad * * escondite, y son sin duda * * miembros diferentes. No tiene sentido verlos como "iguales" en absoluto. – Ani

+0

Bueno, obviamente, sólo lo expresó tan mal - que sería muy difícil escribir código de comparación para devolver cierto en esa circunstancia ;-) - sigue siendo un camino infeliz que necesita pruebas, tal como se me ocurre código de comparación estructural que devolvería cierto . –

+1

¿Puedes aclarar mejor la pregunta? ¿Desea 'string.GetHashCode == int.GetHashCode' (porque sus definiciones base están en el mismo tipo -' object'). ¿Qué tal 'List .Count' contra' HashSet .Count' (ambos implementan 'ICollection .Count')? – Ani

Respuesta

3

Echando un vistazo a los objetos de su PropertyInfo ejemplo IFoo/Foo, podemos llegar a estas conclusiones:

  1. No hay manera directa para ver qué clase/interfaz de la propiedad fue declarada el principio.
  2. Por lo tanto, para comprobar si la propiedad era de hecho declarada en una clase ancestro necesitamos iterar sobre los antepasados ​​y ver si existe la propiedad sobre ellos también.
  3. Igual va para las interfaces, necesitamos llamar al Type.GetInterfaces y trabajar desde allí. No olvide que las interfaces pueden implementar otras interfaces, por lo que esto debe ser recursivo.

Así que vamos a tener una oportunidad. En primer lugar, para cubrir las propiedades heredadas:

PropertyInfo GetRootProperty(PropertyInfo pi) 
{ 
    var type = pi.DeclaringType; 

    while (true) { 
     type = type.BaseType; 

     if (type == null) { 
      return pi; 
     } 

     var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance | 
        BindingFlags.Public | BindingFlags.Static; 
     var inheritedProperty = type.GetProperty(pi.Name, flags); 

     if (inheritedProperty == null) { 
      return pi; 
     } 

     pi = inheritedProperty; 
    } 
} 

Ahora, para cubrir propiedades declaradas en las interfaces (buscar con DFS):

PropertyInfo GetImplementedProperty(PropertyInfo pi) 
{ 
    var type = pi.DeclaringType; 
    var interfaces = type.GetInterfaces(); 

    if (interfaces.Length == 0) { 
     return pi; 
    } 

    var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public; 
    var query = from iface in interfaces 
       let implementedProperty = iface.GetProperty(pi.Name, flags) 
       where implementedProperty != pi 
       select implementedProperty; 

    return query.DefaultIfEmpty(pi).First(); 
} 

Atar estos juntos:

PropertyInfo GetSourceProperty(PropertyInfo pi) 
{ 
    var inherited = this.GetRootProperty(pi); 
    if (inherited != pi) { 
     return inherited; 
    } 

    var implemented = this.GetImplementedProperty(pi); 
    if (implemented != pi) { 
     return implemented; 
    } 

    return pi; 
} 

Esto debería funcionar. No toma en cuenta las propiedades indexados con el mismo nombre pero diferentes tipos y/o número de parámetros de indexación, por lo que queda como ejercicio para el lector proverbiales.

Descargo de responsabilidad: Ni siquiera compilé esto (no hay tiempo para ejecutar pruebas en este momento). Está pensado como punto de partida para "la" respuesta, ya que no hay ninguno hasta el momento.

+0

Sigue siendo un excelente comienzo. Tengo un conjunto de pruebas y un montón de mi (como aún sin trabajar pero similar) para probarlo con - Leo su publicación y veré dónde están las diferencias y le responderé :-) –

+0

Bah, hasta ahora todo bien, pero hay una diferencia entre Mono y MS.NET, así que no puedo verificar completamente (no puedo ejecutar pruebas en mi máquina virtual de Windows por alguna razón) - Lo pondré en funcionamiento y aceptaré su respuesta una vez que haya verificado los casos extremos –

+0

Bien, casi allí - FYI No es lo suficientemente bueno hacer un GetP roperty, tenemos que hacer coincidir la propiedad utilizando el InterfaceMap - aparte de que parece kosher hasta el momento –

1

Parece que sería más sencillo para comprobar los tipos declarando de los dos MemberInfo s que desea comparar. En el caso de una relación de base/subclase, deben representar la misma declaración si los tipos de declaración son los mismos.En el caso de las interfaces, deben ser los mismos si el tipo de interfaz que se declara está en la lista de interfaces del otro:

Type type1 = methodInfo1.DeclaringType; 
Type type2 = methodInfo2.DeclaringType; 

bool same = type1 == type2 || 
    type1.IsInterface && type2.GetInterfaces.Contains(type1) || 
    type2.IsInterface && type1.GetInterfaces.Contains(type2); 

Una cosa a tener en cuenta para las interfaces es el 'mapeo interfaz' - Type.GetInterfaceMap lo que significa Es posible que los métodos declarados en una interfaz no tengan el mismo nombre en la clase de implementación que su enfoque actual no parece tener en cuenta.

+0

Esto todavía devolverá falso si uno de los miembros está anulando un miembro base (en mi definición son los mismos), y por supuesto volverá a ser cierto si los dos tipos son completamente diferentes pero tienen los mismos tipos; sin embargo, esto es solo un error tipográfico porque usted acaba de dejar esto en una respuesta:) - Sin embargo, este punto es muy simple –

+1

Me parece que la solución más larga es la mejor: encontrar la raíz de AMBOS miembros, y compararlos, basándome en comparar memberOne/memberTwo directamente parece ser un error propenso a lo mejor –

2

No estoy exactamente seguro de lo que necesita para este pero supongo que su definición de igualdad en este caso es "hacer las dos informaciones método de invocación el mismo método si se invoca"? Si es así, entonces usted realmente necesita especificar un tipo en la comparación también, por ejemplo, considere esto:

public interface IFoo 
{ 
    void AMethod(); 
} 

public interface IBar 
{ 
    void AMethod(); 
} 

public class FooBar : IFoo, IBar 
{ 
    void AMethod(); 
} 

Claramente typeof (IFoo) .GetMethod ("AMethod") y typeof (IBar) .GetMethod ("AMethod") no son iguales, no son incluso relacionada, PERO que hacen invocar el mismo método en una instancia de FooBar. Así que lo que te gustaría es un método de comparación que tiene tres argumentos:

bool WillInvokeSameMethodOnType(MethodInfo method1, MethodInfo method2, Type type) 

Mira la MethodInfoManager clase en FakeItEasy, podría ser lo que quiere: http://code.google.com/p/fakeiteasy/source/browse/Source/FakeItEasy/Core/MethodInfoManager.cs?r=8888fefbc508fb02d5435a3e33774500bec498b3

+0

Making el tipo disponible sería bastante difícil, pero DeclaringType, etc. ¿sería suficiente? Creo que MethodInfoManager se acerca - pero, ¿cómo manejas un nuevo vs virtual? –

+0

No estoy seguro exactamente de lo que está preguntando, no hay necesidad de un caso especial nuevo vs virtual en mi caso ya que todo lo que me interesa es saber si las infos de método enviarían el mismo método. –

2

Así, este fue un hueso duro de roer y antes Voy a entrar en detalles aburridos Voy a decir esto, terminé optando por hacer una comparación estructural, ya que el único lugar que se caerá, es cuando un miembro oculta otro miembro con la palabra clave 'nueva' en C# - Decidí que era un pequeño problema en este sistema en comparación con la multitud de otras heridas que una solución adecuada para este problema e nds up causando si sale mal (y does van mal, créanme).

acepté una respuesta anterior, ya que estuvo cerca de la solución del problema - el único problema que tiene es que es todavía de naturaleza estructural (y puedo lograr que por tipo/nombre/cheque argumento)

hay algunas diferencias, sin embargo, sobre todo que es necesario tener en cuenta la correlación de interfaz en lugar de llamar GetProperty - esto es todo un detalle importante - aquí está mi método revisado que va a caer encima en ciertas circunstancias.

private PropertyInfo GetImplementedProperty(PropertyInfo pi) 
    { 
     var type = pi.DeclaringType; 
     var interfaces = type.GetInterfaces(); 

     for(int interfaceIndex = 0; interfaceIndex < interfaces.Length; interfaceIndex++) 
     { 
      var iface = interfaces[interfaceIndex]; 
      var interfaceMethods = type.GetInterfaceMap(iface).TargetMethods; 

      MethodInfo matchingMethod = null; 
      for (int x = 0; x < interfaceMethods.Length; x++) 
      { 
       if (pi.GetGetMethod().LooseCompare(interfaceMethods[x]) || pi.GetSetMethod().LooseCompare(interfaceMethods[x])) 
       { 
        matchingMethod = type.GetInterfaceMap(iface).InterfaceMethods[x]; 
        break; 
       } 
      } 
      if (matchingMethod == null) continue; 

      var interfacePi = from i in interfaces 
           from property in i.GetProperties() 
           where property.GetGetMethod().LooseCompare(matchingMethod) || property.GetSetMethod().LooseCompare(matchingMethod) 
           select property; 

      return interfacePi.First(); 
     } 

     return pi; 
    } 

que terminó renunciando a la comprobación de si un miembro se ocultaba otro miembro, y se fue para el siguiente truco:

private PropertyInfo GetRootProperty(PropertyInfo pi) 
    { 
     if ((pi.GetGetMethod().Attributes & MethodAttributes.Virtual) != MethodAttributes.Virtual) { return pi; } 

     var type = pi.DeclaringType; 

     while (true) 
     { 
      type = type.BaseType; 

      if (type == null) 
      { 
       return pi; 
      } 

      var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance | 
         BindingFlags.Public | BindingFlags.Static; 

      var inheritedProperty = type.GetProperty(pi.Name, flags); 

      if (inheritedProperty == null) 
      { 
       return pi; 
      } 

      pi = inheritedProperty; 
     } 
    } 

hago una suposición aquí que una propiedad/método que utiliza la 'nueva 'la palabra clave no usará también la palabra clave virtual, ya que' nueva 'es un poco un caso extremo, esto es bastante improbable.

Esto es por lo que me dieron antes de decidir que se hizo pasar mis pruebas y yo estaba contento con eso. (Y antes decidí simplemente optar por un control estructural ...) Espero que esto sea útil para cualquiera que tropiece de esta manera en el futuro.

Cuestiones relacionadas