How can I inherit the target DebuggerTypeProxy class from base proxies?

Question: I am looking for a way to simplify the creation of a debugger proxy for legacy classes. Thus, when debugging a class that inherits from another, I should see the properties side by side: the basic properties of the base class, as well as the new properties of the parent class.

Here is what I have tried so far:

  • NewA type proxies inherit from type A Properties are not displayed side by side; basic properties are umbrella [sic] under Base . *****
  • Including property A in NewA , which simply translates the current NewA to A , with [DebuggerBrowsable(RootHidden)] : Visual Studio freezes :(

I know that I can simply add the properties of the base class to the NewA , but I try to avoid this. This works too much for classes with many properties.


Explanation:

I use the DebuggerTypeProxy attribute on some of my classes, so I can control how the class looks when viewed during debugging. For example:

 public class A { private String _someField; public String SomeField { get {return _someField;} } } 

By default, tooltip debugging information is displayed as:

enter image description here

... so I use DebuggerTypeProxy to hide the support field:

 [DebuggerTypeProxy(typeof(AProxy))] public class A { // ... internal class AProxy { A _a; AProxy (A a){ _a = a; } public String SomeField { get {return _a.SomeField;} } } } 

... everything is right with the world:

enter image description here


Now I am creating a class that inherits from A.

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

Unfortunately, when debugging this class, Visual Studio uses a base type proxy server (from A ). This means that we can see the underlying SomeField property, but our new AnotherField property AnotherField hidden (unless you AnotherField Raw View , of course):

enter image description here

Removing a type proxy from base A results in AnotherField , but not SomeField .


* Failed to try # 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; } } } } 

Result:

enter image description here

Still not working. Basic properties are not placed side by side with new properties.

+7
source share
1 answer

After hours of searching and messing around, I found a solution - and a beautiful one in that - from the Jared Par blog . It creates a proxy type that uses reflection to compact all members into a single list. Some of the additional magic of DebuggerDisplay does this so that you don’t even notice.

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

<h / "> Modifications

I made three small modifications to the class to make it more convenient for me.

At first, I wanted the participants to sort by name. To do this, change the last line to:

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

Secondly, in the Member structure, I added some attributes so that when expanding the reference class, only the value is displayed:

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

Third, the default flags are BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance means that even items marked with [DebuggerBrowsable(DebuggerBrowsableState.Never)] will be displayed. To prevent this from happening, after this line:

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

add this:

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

Now DebuggerBrowsable(DebuggerBrowsableState.Never) will be respected for fields. You can also add this code to a foreach loop that processes properties so that it also takes properties into account.

+5
source

All Articles