使用泛型创建只读集合
?
4.9 使用泛型創建只讀集合
問題
您希望類中的一個集合里的信息可以被外界訪問,但不希望用戶改變這個集合。
解決方案
使用ReadOnlyCollection<T>包裝就很容易實現只讀的集合類。例子如,Lottery類包含了中獎號碼,它可以被訪問,但不允許被改變:
public?class?Lottery????{
????????//?創建一個列表.
????????List<int>?_numbers?=?null;
????????public?Lottery()
????????{
????????????//?初始化內部列表
????????????_numbers?=?new?List<int>(5);
????????????//?添加值
????????????_numbers.Add(17);
????????????_numbers.Add(21);
????????????_numbers.Add(32);
????????????_numbers.Add(44);
????????????_numbers.Add(58);
????????}
????????public?ReadOnlyCollection<int>?Results
????????{
????????????//?返回一份包裝后的結果
????????????get?{?return?new?ReadOnlyCollection<int>(_numbers);?}
????????}
}
?
Lottery有一個內部的List<int>,它包含了在構造方法中被填的中獎號碼。有趣的部分是它有一個公有屬性叫Results,通過返回的ReadOnlyCollection<int>類型可以看到其中的中獎號碼,從而使用戶通過返回的實例來使用它。
如果用戶試圖設置集合中的一個值,將引發一個編譯錯誤:
Lottery?tryYourLuck?=?new?Lottery();????//?打印結果.
????for?(int?i?=?0;?i?<?tryYourLuck.Results.Count;?i++)
????{
????????Console.WriteLine("Lottery?Number?"?+?i?+?"?is?"?+?tryYourLuck.Results[i]);?
????}
????//?改變中獎號碼!
????tryYourLuck.Results[0]=29;
????//最后一行引發錯誤://?Error?26?//?Property?or?indexer
????//?'System.Collections.ObjectModel.ReadOnlyCollection<int>.this[int]'
????//?cannot?be?assigned?to?--?it?is?read?only
?
討論
ReadOnlyCollection的主要優勢是使用上的靈活性,可以在任何支持IList或IList<T>的集合中把它做為接口使用。ReadOnlyCollection還可以象這樣包裝一般數組:
int?[]?items?=?new?int[3];????items[0]=0;
????items[1]=1;
????items[2]=2;
new?ReadOnlyCollection<int>(items);
?
這為類的只讀屬性的標準化提供了一種方法,并使得類庫使用人員習慣于這種簡單的只讀屬性返回類型。
閱讀參考
查看MSDN文檔中的“IList”和“Generic IList”主題。
4.10 使用相應的泛型版本替換Hashtable
問題
您希望通過使用相應的泛型版本替換所有Hashtable來增強應用程序性能并使得代碼更為易讀。當您發現這些數據結構中存放結構體和值類型會導致裝箱/拆箱操作,這就變得非常有必要了。
解決方案
替換所有已存在的System.Collections.Hashtable類為速度更快的System.Collections.Generic.Dictionary泛型類。
這有一個使用System.Collections.Hashtable對象的簡單例子:
public?static?void?UseNonGenericHashtable()????{
????????//?創建并填充一個Hashtable.
????????Hashtable?numbers?=?new?Hashtable();
????????numbers.Add(1,?"one");????//?鍵會導致裝箱操作
????????numbers.Add(2,?"two");????//?鍵會導致裝箱操作
????????//?在Hashtable顯示所有的鍵/值對.
????????//?在每次迭代中都會因為鍵導致一個拆箱操作
????????foreach?(DictionaryEntry?de?in?numbers)
????????{
????????????Console.WriteLine("Key:?"?+?de.Key?+?"\tValue:?"?+?de.Value);
????????}
????????numbers.Clear();
}
?
下面是相同的代碼使用了System.Collections.Generic.Dictionary<T,U>對象:
public?static?void?UseGenericDictionary()????{
????????//?創建并填充字典.
????????Dictionary<int,?string>?numbers?=?new?Dictionary<int,?string>();
????????numbers.Add(1,?"one");
????????numbers.Add(2,?"two");
????????//?顯示字典中的所有鍵值對.
????????foreach?(KeyValuePair<int,?string>?kvp?in?numbers)
????????{
????????????Console.WriteLine("Key:?"?+?kvp.Key?+?"\tValue:?"?+?kvp.Value);
????????}
????????numbers.Clear();
}
?
討論
對于應用程序中簡單的Hashtable實現,這種替換將十分容易。但有些地方需要注意,如泛型Dictionary類沒有實現ICloneable接口,而Hashtable類實現了。
表4-2顯示了兩個類中的等價成員:
表4-2 Hashtable和泛型Dictionary類的等價成員
| Hashtable 類的成員 | 泛型 Dictionary 類的相應成員 |
| N/A | Comparer 屬性 |
| Count屬性 | Count屬性 |
| IsFixedSize屬性 | ((IDictionary)myDict).IsFixedSize |
| IsReadOnly屬性 | ((IDictionary)myDict).IsReadOnly |
| IsSynchronized屬性 | ((IDictionary)myDict).IsSynchronized |
| Item屬性 | Item屬性 |
| Keys屬性 | Keys屬性 |
| SyncRoot屬性 | ((IDictionary)myDict).SyncRoot |
| Values屬性 | Values屬性 |
| Add 方法 | Add方法 |
| Clear方法 | Clear方法 |
| Clone方法 | 在重載構造方法中接收一個 IDictionary<T,U> 類型 |
| Contains方法 | ContainsKey方法 |
| ContainsKey方法 | ContainsKey方法 |
| ContainsValue方法 | ContainsValue方法 |
| CopyTo方法 | ((ICollection)myDict).CopyTo(arr,0) |
| Remove方法 | Remove方法 |
| Synchronized static方法 | lock(myDictionary.SyncRoot) {…} |
| N/A | TRyGetValue方法 |
?
表4-2中,并非所有的Hashtable和Dictionary的成員都一一對應。我們從屬性開始,注意,只有Count,Keys,Values和Item屬性在兩個類中都存在。為了彌補Dictionary中缺少的屬性,需要把它轉化為IDictionary類型。下面的代碼演示了如果進行這些轉換以獲得缺少的屬性:
Dictionary<int,?string>?numbers?=?new?Dictionary<int,?string>();????Console.WriteLine(((IDictionary)numbers).IsReadOnly);
????Console.WriteLine(((IDictionary)numbers).IsFixedSize);
????Console.WriteLine(((IDictionary)numbers).IsSynchronized);
Console.WriteLine(((IDictionary)numbers).SyncRoot);
?
注意,由于缺少返回一個泛型字典同步版本的代碼,IsSynchronized屬性將總是返回false。SyncRoot屬性在被調用時總是返回相同的對象。實際上這個屬性返回的是this指針。微軟已經決定移除泛型集合類的創建同步包裝的功能。
做為替代,他們推薦使用lock關鍵字鎖住整個集合或其他同步對象類型以滿足您的需求。
因為在泛型字典類中也缺少了克隆方法(實際是是因為這個類沒有實現ICloneable接口),您可以轉而使用重載的構造方法來接收一個IDictionary<T,U>類型:
?//?創建并填充字典.????Dictionary<int,?string>?numbers?=?new?Dictionary<int,?string>();
????numbers.Add(1,?"one");
????numbers.Add(2,?"two");
????//?顯示原字典的鍵/值對.
????foreach?(KeyValuePair<int,?string>?kvp?in?numbers)
????{
????????Console.WriteLine("Original?Key:?"?+?kvp.Key?+?"\tValue:?"?+?kvp.Value);
????}
????//?克隆字典對象.
????Dictionary<int,?string>?clonedNumbers?=?new?Dictionary<int,?string>(numbers);
????//?顯示克隆字典中的鍵/值對.
????foreach?(KeyValuePair<int,?string>?kvp?in?numbers)
????{
????????Console.WriteLine("Cloned?Key:?"?+?kvp.Key?+?"\tValue:?"?+?kvp.Value);
}
?
還有兩個Dictionary類中缺少的方法:Contains和CopyTo方法。Contains方法的功能在Dictionary類中很容易被實現。在Hashtable類中,Cintains方法和ContainsKey方法有相同的行為,因此您可以在Dictionary類中簡單地使用ContainsKey方法來模擬Hashtable類中的Contains方法:
?//?創建和填充字典.????Dictionary<int,?string>?numbers?=?new?Dictionary<int,?string>();
????numbers.Add(1,?"one");
????numbers.Add(2,?"two");
????Console.WriteLine("numbers.ContainsKey(1)?==?"?+?numbers.ContainsKey(1));?
Console.WriteLine("numbers.ContainsKey(3)?==?"?+?numbers.ContainsKey(3));
CopyTo方法也很容易在Dictionary類中被模擬,但需要做一些額外的工作:
????//?創建和填充字典.
????Dictionary<int,?string>?numbers?=?new?Dictionary<int,?string>();
????numbers.Add(1,?"one");
????numbers.Add(2,?"two");
????//?顯示字典中的所有鍵/值對.
????foreach?(KeyValuePair<int,?string>?kvp?in?numbers)
????{
????????Console.WriteLine("Key:?"?+?kvp.Key?+?"\tValue:?"?+?kvp.Value);
????}
????//?創建對象數組來拷貝字典對象中的信息.?
????KeyValuePair<int,?string>[]?objs?=?new?KeyValuePair<int,?
string>[numbers.Count];
????//?調用字典中的CopyTo方法
????//?把字典中的所有鍵/值對對象拷貝到objs中
????((IDictionary)numbers).CopyTo(objs,?0);
????//?顯示objs[]中的所有鍵/值對.
????foreach?(KeyValuePair<int,?string>?kvp?in?objs)
????{
????????Console.WriteLine("Key:?"?+?kvp.Key?+?"\tValue:?"?+?kvp.Value);
}
?
調用Dictionary對象中的CopyTo方法需要創建一個KeyValuePair<T,U>對象數組,它用于在CopyTo方法被調用之后,控制字典對象中的所有KeyValuePair<T,U>對象。接下來numbers字典對象被轉換為IDictionary類型以調用CopyTo方法。一旦CopyTo方法被調用,objs數組將包含原numbers對象中的所有KeyValuePair<T,U>對象。注意objs數組迭代時使用了foreach循環,這點和numbers對象是相同的。
閱讀參考
查看MSDN文檔中的“System.Collections.Hashtable Class”和“System.Collections.Generic.Dictionary Class"主題。
與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的使用泛型创建只读集合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 2841 Visible Tre
- 下一篇: 拽 Excel 到 ComponentO