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