¿Cómo uso la reflexión para obtener propiedades que implementan explícitamente una interfaz?

StackOverflow https://stackoverflow.com/questions/278997

Pregunta

Más específicamente, si tengo:

public class TempClass : TempInterface
{

    int TempInterface.TempProperty
    {
        get;
        set;
    }
    int TempInterface.TempProperty2
    {
        get;
        set;
    }

    public int TempProperty
    {
        get;
        set;
    }
}

public interface TempInterface
{
    int TempProperty
    {
        get;
        set;
    }
    int TempProperty2
    {
        get;
        set;
    }
}

¿Cómo uso la reflexión para obtener todos los PropertyInfos para propiedades que implementan explícitamente TempInterface?

Gracias.

¿Fue útil?

Solución 5

Tuve que modificar la respuesta de Jacob Carpenter pero funciona muy bien. nobugz también funciona, pero Jacobs es más compacto.

var explicitProperties =
from method in typeof(TempClass).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
where method.IsFinal && method.IsPrivate
select method;

Otros consejos

Creo que la clase que está buscando es System.Reflection.InterfaceMapping.

Type ifaceType = typeof(TempInterface);
Type tempType = typeof(TempClass);
InterfaceMapping map = tempType.GetInterfaceMap(ifaceType);
for (int i = 0; i < map.InterfaceMethods.Length; i++)
{
    MethodInfo ifaceMethod = map.InterfaceMethods[i];
    MethodInfo targetMethod = map.TargetMethods[i];
    Debug.WriteLine(String.Format("{0} maps to {1}", ifaceMethod, targetMethod));
}

El captador y definidor de propiedades de una propiedad de interfaz implementada explícitamente tiene un atributo inusual. Su propiedad IsFinal es True, incluso cuando no es miembro de una clase sellada. Pruebe este código para verificar mi afirmación:

  foreach (AssemblyName name in Assembly.GetEntryAssembly().GetReferencedAssemblies()) {
    Assembly asm = Assembly.Load(name);
    foreach (Type t in asm.GetTypes()) {
      if (t.IsAbstract) continue;
      foreach (MethodInfo mi in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) {
        int dot = mi.Name.LastIndexOf('.');
        string s = mi.Name.Substring(dot + 1);
        if (!s.StartsWith("get_") && !s.StartsWith("set_")) continue;
        if (mi.IsFinal)
          Console.WriteLine(mi.Name);
      }
    }
  }

Aquí hay una solución modificada basada en la implementación dada en esta publicación de blog :

var explicitProperties =
    from prop in typeof(TempClass).GetProperties(
        BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
    let getAccessor = prop.GetGetMethod(true)
    where getAccessor.IsFinal && getAccessor.IsPrivate
    select prop;

foreach (var p in explicitProperties)
    Console.WriteLine(p.Name);

Basándose en respuesta de MrKurt :

var targetMethods =
    from iface in typeof(TempClass).GetInterfaces()
    from method in typeof(TempClass).GetInterfaceMap(iface).TargetMethods
    select method;

var explicitProps =
    from prop in typeof(TempClass).GetProperties(BindingFlags.Instance |
                                                 BindingFlags.NonPublic)
    where targetMethods.Contains(prop.GetGetMethod(true)) ||
          targetMethods.Contains(prop.GetSetMethod(true))
    select prop;

Es demasiado complejo. Debe reflexionar sobre los métodos / propiedades del tipo de interfaz, ver si existen en su tipo de clase y compararlos para ver si son los mismos '' cuando existen.

Si hay algo en la interfaz pero no del tipo que está probando, es una implementación explícita. Si está en ambos, pero es diferente entre los dos, es una interfaz explícita.

Al código de Jacob le falta un filtro:

        var props = typeof(TempClass).GetInterfaces().Where(i => i.Name=="TempInterface").SelectMany(i => i.GetProperties());
        foreach (var prop in props)
            Console.WriteLine(prop);

¡Esto parece un poco doloroso sin razón aparente!

Mi solución es para el caso en el que conoce el nombre de la propiedad que está buscando y es bastante simple.

Tengo una clase para hacer la reflexión un poco más fácil que solo tuve que agregar este caso a:

public class PropertyInfoWrapper
{
    private readonly object _parent;
    private readonly PropertyInfo _property;

    public PropertyInfoWrapper(object parent, string propertyToChange)
    {
        var type = parent.GetType();
        var privateProperties= type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);

        var property = type.GetProperty(propertyToChange) ??
                       privateProperties.FirstOrDefault(p => UnQualifiedNameFor(p) == propertyName);

        if (property == null)
            throw new Exception(string.Format("cant find property |{0}|", propertyToChange));

        _parent = parent;
        _property = property;
    }

    private static string UnQualifiedNameFor(PropertyInfo p)
    {
        return p.Name.Split('.').Last();
    }

    public object Value
    {
        get { return _property.GetValue(_parent, null); }
        set { _property.SetValue(_parent, value, null); }
    }
}

No puede simplemente hacer == en el nombre porque las propiedades implementadas explícitamente tienen nombres completamente calificados.

GetProperties necesita los dos indicadores de búsqueda para acceder a propiedades privadas.

Una clase auxiliar simple que podría ayudar:

public class InterfacesPropertiesMap
{
    private readonly Dictionary<Type, PropertyInfo[]> map;

    public InterfacesPropertiesMap(Type type)
    {
        this.Interfaces = type.GetInterfaces();
        var properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

        this.map = new Dictionary<Type, PropertyInfo[]>(this.Interfaces.Length);

        foreach (var intr in this.Interfaces)
        {
            var interfaceMap = type.GetInterfaceMap(intr);
            this.map.Add(intr, properties.Where(p => interfaceMap.TargetMethods
                                                    .Any(t => t == p.GetGetMethod(true) ||
                                                              t == p.GetSetMethod(true)))
                                         .Distinct().ToArray());
        }
    }

    public Type[] Interfaces { get; private set; }

    public PropertyInfo[] this[Type interfaceType]
    {
        get { return this.map[interfaceType]; }
    }
}

Obtendrá propiedades para cada interfaz, incluso implementadas explícitamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top