2012-09-16 7 views
7

Pregunta: Estoy buscando una manera de simplificar la construcción de proxys tipo depurador para las clases heredadas. Por lo tanto, al depurar una clase que hereda de otra, debería ver las propiedades de ambas, una al lado de la otra: las propiedades base de la clase base, más las nuevas propiedades de la clase padre.¿Cómo puedo hacer que mi clase de destino DebuggerTypeProxy herede los proxies base?

Aquí es lo que he probado hasta ahora:

  1. NewA 's tipo de proxy hereda de la de A. Las propiedades no se muestran una al lado de la otra; las propiedades base son umbrella'd [sic] bajo Base. *****
  2. La inclusión de una característica en ANewA que simplemente pone la corriente NewA a A, con [DebuggerBrowsable(RootHidden)]: Visual Studio se bloquea :(

Sé que solo podría añadir propiedades para la base clase en representación NewA 's, pero estoy tratando de evitar esto es demasiado trabajo para las clases con muchas propiedades


Explicación:..

Uso el atributo DebuggerTypeProxy en algunas de mis clases para poder controlar cómo se ve la clase cuando se examina durante la depuración. Por ejemplo:

public class A { 
    private String _someField; 

    public String SomeField { 
     get {return _someField;} 
    } 
} 

Por defecto, la información sobre herramientas muestra información de depuración como:

enter image description here

... así que utilizo un DebuggerTypeProxy para ocultar el campo respaldo:

[DebuggerTypeProxy(typeof(AProxy))] 
public class A { 
    // ... 

    internal class AProxy { 
     A _a; 
     AProxy (A a){ 
      _a = a; 
     } 

     public String SomeField { 
      get {return _a.SomeField;} 
     } 
    } 
} 

... todo está bien con el mundo:

enter image description here


Ahora, puedo crear una clase que hereda de A.

public class NewA : A { 
    private String _anotherField; 
    public String AnotherField { 
     get {return _anotherField;} 
    } 
} 

Por desgracia, cuando la depuración de esta clase, Visual Studio utiliza el tipo de base de proxy (de A). Esto significa que podemos ver la propiedad de base SomeField, pero nuestro nuevo AnotherField propiedad está escondida (a menos que se expande Raw View, por supuesto):

enter image description here

Extracción del tipo de proxy de la base A resultados en AnotherField muestra, pero no SomeField.


* intento fallido # 1

/// <summary> 
/// The base class 
/// </summary> 
[DebuggerTypeProxy(typeof(AProxy))] 
public class A { 
    private String _someField; 

    public String SomeField { 
     get { return _someField; } 
    } 

    protected class AProxy { 
     A _a; 
     protected AProxy(A a) { 
      _a = a; 
     } 

     String SomeField { 
      get { return _a.SomeField; } 
     } 
    } 

} 

/// <summary> 
/// Parent class 
/// </summary> 
[DebuggerTypeProxy(typeof(NewAProxy))] 
public class NewA : A { 
    private String _anotherField; 
    public String AnotherField { 
     get { return _anotherField; } 
    } 

    // Inherit base type proxy, in an effort to display base properties 
    // side-by-side with AnotherField: Doesn't work. 
    protected class NewAProxy : A.AProxy { 
     NewA _newA; 
     protected NewAProxy(NewA newA) 
      : base(newA) { 
      _newA = newA; 
     } 

     public String AnotherField { 
      get { return _newA.AnotherField; } 
     } 
    } 
} 

Resultado:

enter image description here

todavía no funciona. Las propiedades base no se colocan lado a lado con las nuevas propiedades.

Respuesta

5

Después de horas de búsqueda y retoques, encontré la solución - y una hermosa en eso - from Jared Par's blog. Él crea un proxy de tipo que usa la reflexión para condensar a todos los miembros en una lista. Un poco de magia adicional DebuggerDisplay lo hace para que no lo notes.

// http://blogs.msdn.com/b/jaredpar/archive/2010/02/19/flattening-class-hierarchies-when-debugging-c.aspx 
// by Jared Par 
internal sealed class FlattenHierarchyProxy { 

    [DebuggerDisplay("{Value}", Name = "{Name,nq}", Type = "{Type.ToString(),nq}")] 
    internal struct Member { 
     internal string Name; 
     internal object Value; 
     internal Type Type; 
     internal Member(string name, object value, Type type) { 
      Name = name; 
      Value = value; 
      Type = type; 
     } 
    } 

    [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    private readonly object _target; 
    [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    private Member[] _memberList; 

    [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] 
    internal Member[] Items { 
     get { 
      if (_memberList == null) { 
       _memberList = BuildMemberList().ToArray(); 
      } 
      return _memberList; 
     } 
    } 

    public FlattenHierarchyProxy(object target) { 
     _target = target; 
    } 

    private List<Member> BuildMemberList() { 
     var list = new List<Member>(); 
     if (_target == null) { 
      return list; 
     } 

     var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; 
     var type = _target.GetType(); 
     foreach (var field in type.GetFields(flags)) { 
      var value = field.GetValue(_target); 
      list.Add(new Member(field.Name, value, field.FieldType)); 
     } 

     foreach (var prop in type.GetProperties(flags)) { 
      object value = null; 
      try { 
       value = prop.GetValue(_target, null); 
      } 
      catch (Exception ex) { 
       value = ex; 
      } 
      list.Add(new Member(prop.Name, value, prop.PropertyType)); 
     } 

     return list; 
    } 
} 


Modificaciones

Hice tres pequeñas modificaciones a la clase para que sea más útil para mí.

Primero, quería que los miembros ordenaran por nombre. Para ello, cambie la última línea a:

return list.OrderBy(m => m.Name).ToList(); 

En segundo lugar, en el Member estructura, añadí algunos atributos de modo que sólo el valor mostraría cuando se expande una clase de referencia:

[DebuggerBrowsable(DebuggerBrowsableState.Never)] 
internal string Name; 
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] 
internal object Value; 
[DebuggerBrowsable(DebuggerBrowsableState.Never)] 
internal Type Type; 

Tercera , los indicadores predeterminados BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance significan que incluso los miembros marcados [DebuggerBrowsable(DebuggerBrowsableState.Never)] seguirán mostrándose. Para evitar que esto suceda, después de esta línea:

foreach (var field in type.GetFields(flags)) { 

añadir lo siguiente:

// Respect DebuggerBrowsableAttributes 
var debuggerBrowsableAtts = field.GetCustomAttributes(typeof(DebuggerBrowsableAttribute), true); 
if (debuggerBrowsableAtts.Count() == 1) { 
    var att = debuggerBrowsableAtts[0] as DebuggerBrowsableAttribute; 
    if (att.State == DebuggerBrowsableState.Never) { 
     continue; 
    } 
} 

Ahora el DebuggerBrowsable(DebuggerBrowsableState.Never) se respetarán los campos. También puede agregar ese código al bucle foreach que maneja propiedades, para que también se respete para las propiedades.

+0

también puede habilitar DebuggerBrowsableAttributes para las propiedades. –

Cuestiones relacionadas