目錄
- 1. 基本使用
- 2. 其他KeyedCollection變種
- 3. 執行一個非抽象的KeyedCollection
返回目錄
1. 基本使用
這個類型貌似有些不起眼,在System.Collections.ObjectModel命名空間內,它在.NET 2.0時被加入,來看他的類型層次:
其實,他就是一個字典,只不過字典的鍵是從值中擷取的,看他的執行中發現內不是有一個Dictionary<TKey, TItem>欄位用來儲存資料,同時他也是支援比較子(IEqualityComparer<TKey>對象)的。但同時他的基類是Collection<TItem>類型,而Collection<TItem>執行了IList<T>, IList這些基本集合操作介面,所以KeyedCollection<TKey, TItem>在操作上會類似一個List<T>,T則代表KeyedCollection<TKey, TItem>中的TItem類型。這樣做的結果是你可以同時使用數組的索引值或者字典的鍵來訪問KeyedCollection的成員!
當然由於KeyedCollection<TKey, TItem>內部有一個Dictionary<TKey, TItem>,同時由於繼承自Collection<TItem>,所以內部也會有一個List<TItem>,所以相對Dictionary<TKey, TItem>類型他會佔用更多空間。
從ILSpy給出的繼承類上看,這個類在WCF中有過應用。
在使用上,比如,我們需要建立一個基於檔案對象(System.IO.FileInfo對象)的字典,字典的鍵就是檔案的路徑。此時就可以使用KeyedCollection類型。注意他是一個抽象類別,因為值怎樣包含鍵是無從得知的,所以需要執行這個抽象類別的GetKeyForItem方法。僅此而已,執行了這個方法後就可以使用KeyedCollection了。(注意本例中由於檔案路徑是不區分大小寫,所以需要在執行時設定IEqualityComparer<string>參數)
代碼:
//+ using System.Collections.ObjectModel
//+ using System.IO
class MyFileDictionary : KeyedCollection<string, FileInfo>
{
//注意檔案路徑是不區分大小寫,所以需要設定IEqualityComparer<string>。
public MyFileDictionary()
: base(StringComparer.OrdinalIgnoreCase)
{ }
//從FileInfo中返迴路徑作為Key。
protected override string GetKeyForItem(FileInfo item)
{
return item.FullName;
}
}
然後測試代碼:
//加入資料
var myFileDic = new MyFileDictionary();
foreach (var fileInfo in DriveInfo.GetDrives()[0].RootDirectory.GetFiles())
myFileDic.Add(fileInfo);
//通過Index和Key擷取值,結果當然會輸出True,因為他們指向同一個對象。
Console.WriteLine(myFileDic[0] == myFileDic[myFileDic[0].FullName]);
返回目錄
2. 其他KeyedCollection變種
首先是安全執行緒的KeyedCollection執行。
注意:對於安全執行緒集合,強烈建議使用.NET 4.0中System.Collections.Concurrent命名空間內提供的高效能安全執行緒集合。讀者可參考MSDN。
事實上在.NET 3.0後,有一個安全執行緒的KeyedCollection執行,它包含在WCF程式集:System.ServiceModel.dll中。引用了這個程式集後,就可以使用System.Collections.Generic命名空間內的同步的KeyedCollection類型:SynchronizedKeyedCollection<K, T>類型了,注意他和KeyedCollection一樣,也是抽象類別。
其次中還有一個需要說的類型,他是KeyedByTypeCollection<TItem>類型,他直接繼承自KeyedCollection,從名字上就可以看出來,這種類型的KeyedCollection的鍵就是從值對象的類型中擷取的。
所以他只保留了TItem,從KeyedCollection<System.Type, TItem>類型中繼承。如:
注意:
在Silverlight 5中的System.ServiceModel.dll中(版本顯示是5.0.5.0):
SynchronizedCollection<T>類型為internal。且沒有SynchronizedKeyedCollection<K, T>類型。
返回目錄
3. 執行一個非抽象的KeyedCollection
由於KeyedCollection是抽象類別,所以每次需要具體的使用,你必須去定義一個繼承KeyedCollection的新類型。有些麻煩,當然,完全可以自己寫一個通用的非抽象KeyedCollection類型執行。使用一個負責轉換的委託欄位就可以。
如下代碼:
class MyKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
{
Func<TItem, TKey> _converter;
public MyKeyedCollection(Func<TItem, TKey> converter, IEqualityComparer<TKey> comparer, int dictionaryCreationThreshold)
: base(comparer, dictionaryCreationThreshold)
{
if (converter == null)
{
throw new ArgumentNullException("converter");
}
_converter = converter;
}
public MyKeyedCollection(Func<TItem, TKey> converter, IEqualityComparer<TKey> comparer)
: this(converter, comparer, 0)
{ }
public MyKeyedCollection(Func<TItem, TKey> converter)
: this(converter, null, 0)
{ }
protected override TKey GetKeyForItem(TItem item)
{
return _converter(item);
}
}
那麼對於最上面那個儲存FileInfo的例子,我們就不需要專門定義新類型了,使用這個MyKeyedCollection類型就可以了,如下代碼:
var myFileDic = new MyKeyedCollection<string, FileInfo>(fileInfo => fileInfo.FullName, StringComparer.OrdinalIgnoreCase);