Bagikan melalui


Beri tahu debugger apa yang harus ditampilkan menggunakan Atribut DebuggerDisplay (C#, Visual Basic, F#, C++/CLI)

DebuggerDisplayAttribute mengontrol bagaimana objek, properti, atau bidang ditampilkan di jendela variabel debugger. Atribut ini dapat diterapkan ke jenis (kelas, struktur, enum, delegasi). Jika diterapkan ke jenis dasar, atribut juga berlaku untuk subkelas.

DebuggerDisplay atribut memiliki argumen tunggal, yang merupakan string yang akan ditampilkan di kolom nilai untuk instans jenis. String ini dapat berisi kurung kurawal ({ dan }). Teks dalam sepasang kurung kurawal dievaluasi sebagai bidang, properti, atau metode.

Jika kelas memiliki metode yang ditimpa ToString(), debugger menggunakan metode yang ditimpa alih-alih default {<typeName>}. Dengan demikian, jika Anda telah mengambil alih metode ToString(), debugger menggunakan metode yang ditimpa alih-alih default {<typeName>}, dan Anda tidak perlu menggunakan DebuggerDisplay. Jika Anda menggunakan keduanya, atribut DebuggerDisplay lebih diutamakan daripada metode yang ToString() ditimpa. Atribut DebuggerDisplay juga lebih diutamakan daripada metode yang ditimpa ToString() dalam subkelas.

Apakah debugger mengevaluasi panggilan implisit ToString() ini bergantung pada pengaturan pengguna dalam kotak dialog Alat / Opsi / Penelusuran kesalahan.

Penting

Jika kotak centang Tampilkan struktur objek mentah di jendela variabel dipilih di kotak dialog Alat/Opsi/Penelusuran Kesalahan, atribut DebuggerDisplay akan diabaikan.

Catatan

Untuk kode asli, atribut ini hanya didukung dalam kode C++/CLI.

Tabel berikut menunjukkan beberapa kemungkinan penggunaan DebuggerDisplay atribut dan contoh output.

Atribut Output tampak di kolom Nilai
[DebuggerDisplay("x = {x} y = {y}")]

Digunakan pada jenis dengan bidang x dan y.
x = 5 y = 18
[DebuggerDisplay("String value is {getString()}")]Sintaks parameter dapat bervariasi di antara bahasa. Karena itu, gunakan dengan hati-hati. String value is [5, 6, 6]

DebuggerDisplay juga dapat menerima parameter bernama.

Parameter Tujuan
Name, Type Parameter ini memengaruhi kolom Nama dan Jenis dari jendela variabel. (Mereka dapat diatur ke string menggunakan sintaks yang sama dengan konstruktor.) Menggunakan parameter ini secara berlebihan, atau menggunakannya dengan tidak benar, dapat menyebabkan output yang membingungkan.
Target, TargetTypeName Menentukan jenis target saat atribut digunakan pada tingkat perakitan.

File autoexp.cs menggunakan atribut DebuggerDisplay di tingkat perakitan. File autoexp.cs menentukan ekspansi default yang Visual Studio gunakan untuk objek .NET. Anda dapat memeriksa file autoexp.cs untuk contoh cara menggunakan atribut DebuggerDisplay, atau Anda dapat memodifikasi dan mengompilasi file autoexp.cs untuk mengubah ekspansi default. Pastikan untuk mencadangkan file autoexp.cs sebelum Anda memodifikasinya.

Untuk membangun autoexp.cs, buka Wantian Perintah Pengembang untuk VS2015, dan jalankan perintah berikut

cd <directory containing autoexp.cs>
csc /t:library autoexp.cs

Perubahan pada autoexp.dll akan dibawa di sesi debug berikutnya.

Menggunakan Ekspresi di DebuggerDisplay

Meskipun Anda dapat menggunakan ekspresi umum antara kurung kurawal dalam atribut DebuggerDisplay, praktik ini tidak disarankan.

Ekspresi umum di DebuggerDisplay memiliki akses implisit ke this pointer untuk tipe target saat ini saja. Ekspresi tidak memiliki akses ke alias, lokal, atau pointer. Jika ekspresi mengacu pada properti, atribut pada properti tersebut tidak diproses. Misalnya, kode C# [DebuggerDisplay("Object {count - 2}")] akan ditampilkan Object 6 jika bidang count adalah 8.

Menggunakan ekspresi di DebuggerDisplay dapat menyebabkan masalah berikut:

  • Mengevaluasi ekspresi adalah operasi termahal dalam debugger dan ekspresi dievaluasi setiap kali ditampilkan. Ini dapat menyebabkan masalah kinerja dalam melangkah melalui kode. Misalnya, ekspresi kompleks yang digunakan untuk menampilkan nilai dalam koleksi atau daftar bisa sangat lambat ketika jumlah elemen besar.

  • Ekspresi dievaluasi oleh evaluator ekspresi bahasa bingkai tumpukan saat ini dan bukan oleh evaluator bahasa tempat ekspresi ditulis. Ini dapat menyebabkan hasil yang tidak dapat diprediksi ketika bahasa berbeda.

  • Mengevaluasi ekspresi dapat mengubah status aplikasi. Misalnya, ekspresi yang menetapkan nilai properti mengubah nilai properti dalam kode yang dieksekusi.

    Salah satu cara untuk mengurangi kemungkinan masalah evaluasi ekspresi adalah dengan membuat properti privat yang melakukan operasi dan mengembalikan string. Atribut DebuggerDisplay kemudian dapat menampilkan nilai properti privat tersebut. Contoh berikut mengimplementasikan pola ini:

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class MyClass
{
    public int count { get; set; }
    public bool flag { get; set; }
    private string DebuggerDisplay
    {
        get
        {
            return string.Format("Object {0}", count - 2);
        }
    }
}

Akhiran ",nq" memberi tahu evaluator ekspresi untuk menghapus tanda kutip saat menampilkan nilai akhir (nq = tanpa tanda kutip).

Contoh

Contoh kode berikut menunjukkan cara menggunakan DebuggerDisplay, bersama dengan DebuggerBrowsable dan DebuggerTypeProxy. Saat dilihat di jendela variabel debugger, seperti jendela Tonton, itu menghasilkan ekspansi yang terlihat seperti ini:

Nama Nilai Jenis
Tombol "three" objek {string}
Value 3 objek {int}
[DebuggerDisplay("{value}", Name = "{key}")]
internal class KeyValuePairs
{
    private IDictionary dictionary;
    private object key;
    private object value;
    public KeyValuePairs(IDictionary dictionary, object key, object value)
    {
        this.value = value;
        this.key = key;
        this.dictionary = dictionary;
    }

    public object Key
    {
        get { return key; }
        set
        {
            object tempValue = dictionary[key];
            dictionary.Remove(key);
            key = value;
            dictionary.Add(key, tempValue);
        }
    }

    public object Value
    {
        get { return this.value; }
        set
        {
            this.value = value;
            dictionary[key] = this.value;
        }
    }
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable
{
    public Hashtable hashtable;

    public MyHashtable()
    {
        hashtable = new Hashtable();
    }

    private string DebuggerDisplay { get { return "Count = " + hashtable.Count; } }

    private class HashtableDebugView
    {
        private MyHashtable myhashtable;
        public HashtableDebugView(MyHashtable myhashtable)
        {
            this.myhashtable = myhashtable;
        }

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public KeyValuePairs[] Keys
        {
            get
            {
                KeyValuePairs[] keys = new KeyValuePairs[myhashtable.hashtable.Count];

                int i = 0;
                foreach (object key in myhashtable.hashtable.Keys)
                {
                    keys[i] = new KeyValuePairs(myhashtable.hashtable, key, myhashtable.hashtable[key]);
                    i++;
                }
                return keys;
            }
        }
    }
}