7

質問: 継承されたクラスのデバッガ型プロキシの構築を簡素化する方法を探しています。したがって、別のクラスから継承するクラスをデバッグする場合、基本クラスの基本プロパティと親クラスの新しいプロパティの両方のプロパティを並べて表示する必要があります。

これが私がこれまでに試したことです:

  1. NewAのタイプ プロキシは のタイプ プロキシを継承しAます。プロパティは並べて表示されません。基本プロパティは [sic] の下にまとめられてBaseいます。*****
  2. 現在をにキャストするだけのAプロパティを含めると、 Visual Studioがハングします:( NewANewAA[DebuggerBrowsable(RootHidden)]

基本クラスのプロパティをNewAのプロキシに追加できることはわかっていますが、これを避けようとしています。多くのプロパティを持つクラスにとっては負担が大きすぎます。


説明:

一部のクラスでこの属性を使用しているDebuggerTypeProxyため、デバッグ中にブラウズしたときにクラスがどのように見えるかを制御できます。例えば:

public class A {
    private String _someField;

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

デフォルトでは、ツールチップのデバッグ情報は次のように表示されます。

ここに画像の説明を入力

...だから、DebuggerTypeProxy を使用してバッキング フィールドを非表示にします。

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

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

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

...すべてが世界で正しいです:

ここに画像の説明を入力


次に、A を継承するクラスを作成します。

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

残念ながら、このクラスをデバッグするとき、Visual Studio はベース タイプ プロキシ (from A) を使用します。これは、基本SomeFieldプロパティを表示できることを意味しますが、新しいAnotherFieldプロパティは非表示になっています (Raw Viewもちろん、展開しない限り)。

ここに画像の説明を入力

タイプ プロキシをベースから削除すると表示Aされますが、AnotherField表示されませんSomeField


*試行失敗 #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; }
        }
    }
}

結果:

ここに画像の説明を入力

それでもうまくいきません。基本プロパティは、新しいプロパティと並べて配置されません。

4

1 に答える 1

6

何時間にもわたる検索と試行錯誤の末、Jared Par のブログから解決策を見つけました。彼は、リフレクションを使用してすべてのメンバーを 1 つのリストに凝縮するタイプ プロキシを作成します。いくつかの追加DebuggerDisplayの魔法がそれを作るので、あなたは気付かない.

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


変更

クラスを使いやすくするために、クラスに 3 つの小さな変更を加えました。

まず、メンバーを名前順に並べ替えます。これを行うには、最後の行を次のように変更します。

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

次に、Member構造体にいくつかの属性を追加して、参照クラスを展開したときに値のみが表示されるようにしました。

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

第 3 に、デフォルトのフラグBindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instanceは、マークされたメンバー[DebuggerBrowsable(DebuggerBrowsableState.Never)]も引き続き表示されることを意味します。これを防ぐには、次の行の後に:

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

これを追加:

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

これで、DebuggerBrowsable(DebuggerBrowsableState.Never)フィールドが尊重されます。また、そのコードをプロパティを処理する foreach ループに追加して、プロパティに対しても尊重されるようにすることもできます。

于 2012-09-16T02:56:31.503 に答える