這篇文章要介紹的是對於自訂集合類別的偵錯輔助類別。
建立一個簡單的自訂集合類別
這個集合類別的設計並不完整,也沒有實作一些相關的介面,單純只是為了介紹偵錯輔助類別而存在的:
public class MyCollection<T>
{
T[] _value;
public int Count { get; set; }
public int Capacity
{
get => _value.Length;
}
public MyCollection()
{
_value = new T[0];
}
public T this[int index]
{
get
{
if (index < 0 || index >= Count)
{
throw new IndexOutOfRangeException();
}
return _value[index];
}
set
{
if (index < 0 || index >= Count)
{
throw new IndexOutOfRangeException();
}
_value[index] = value;
}
}
public void Add(T item)
{
if (Count == Capacity)
{
var newArray = new T[Capacity == 0 ? 4 : 2 * Capacity];
for (int i = 0; i < Count; i++)
{
newArray[i] = _value[i];
}
_value = newArray;
}
_value[Count] = item;
Count++;
}
}
和既有的 List<T> 在偵錯時期工具視窗資訊內容的比較
先建立一個簡單的實驗用程式碼:
static void Main(string[] args)
{
var list = new List<int>() { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
Console.WriteLine(list.Count);
var collection = new MyCollection<int>();
for (int i = 0; i < 10; i++)
{
collection.Add(i);
}
Console.WriteLine(collection.Count);
}
把中斷點下在兩個 Console.WriteLine 的位置。
觀察這兩者的資訊內容有何不同?
(1) 在 List<int> 中斷的時候看到的資訊是這樣:
(2) 而在 MyCollection<int> 看到的資訊卻是這樣:
乍看好像沒甚麼不一樣,但仔細瞧瞧,在 List<int> 看到的是有效的資訊,也就是只有出現 Add 進來的10個元素 ;但在 MyCollection<int> 你看到的 _value 會有16個元素,但其實有效的元素是 10 個,這表示目前的偵錯資訊和期待是不相符的。
註:事實上 List<int> 裡的內部儲存陣列裡是16個元素,如果你真想看到,點下未經處理的檢視就有了。
建立輔助類別協助正確展現偵錯資訊
public class MyCollectionDebugView<T>
{
private MyCollection<T> _collection;
public MyCollectionDebugView(MyCollection<T> collection)
{
_collection = collection;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items
{
get
{
T[] items = new T[_collection.Count];
for (int i = 0; i < _collection.Count; i++)
{
items[i] = _collection[i];
}
return items;
}
}
}
這個類別主要是傳入要觀察資訊的 MyCollection<T> 執行個體,然後靠索引子列出資訊,在索引子上必須加入 DebuggerBrowsableAttribute,並且設定為 RootHidden 隱藏根元素 (不同的 DebuggerBrowsableState 會有不同效果,有興趣的朋友可以自己試試看)。
接著在 MyCollection<T> class 透過 DebuggerTypeProxyAttribute 套用這個 MyCollectionDebugView 。
[DebuggerTypeProxy(typeof (MyCollectionDebugView<>))]
public class MyCollection<T>
現在能看到的資訊變成這樣:
好像少了點甚麼?在 List<T> 的偵錯資訊裡還多出顯示一個 Count 屬性,所以再補上一個 Attribute:
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof (MyCollectionDebugView<>))]
public class MyCollection<T>
這樣偵錯資訊的顯示就非常接近 List<T> 的樣子了。
以後如果有機會自訂集合型別,記得要幫它建立偵錯輔助類別和套用 Debugger 相關的 Attributes,這樣設計出來的型別的使用體驗會更好喔。