Menerapkan generik OrderedDictionary
tidak terlalu sulit, tetapi memakan waktu yang tidak perlu dan terus terang kelas ini merupakan pengawasan besar di pihak Microsoft. Ada beberapa cara untuk menerapkan ini, tetapi saya memilih menggunakan a KeyedCollection
untuk penyimpanan internal saya. Saya juga memilih untuk menerapkan berbagai metode untuk menyortir dengan cara yang List<T>
dilakukan karena ini pada dasarnya adalah IList dan IDictionary hybrid. Saya telah memasukkan implementasi saya di sini untuk anak cucu.
Berikut antarmukanya. Perhatikan bahwa itu termasuk System.Collections.Specialized.IOrderedDictionary
, yang merupakan versi non-generik dari antarmuka ini yang disediakan oleh Microsoft.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace mattmc3.Common.Collections.Generic {
public interface IOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IOrderedDictionary {
new TValue this[int index] { get; set; }
new TValue this[TKey key] { get; set; }
new int Count { get; }
new ICollection<TKey> Keys { get; }
new ICollection<TValue> Values { get; }
new void Add(TKey key, TValue value);
new void Clear();
void Insert(int index, TKey key, TValue value);
int IndexOf(TKey key);
bool ContainsValue(TValue value);
bool ContainsValue(TValue value, IEqualityComparer<TValue> comparer);
new bool ContainsKey(TKey key);
new IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
new bool Remove(TKey key);
new void RemoveAt(int index);
new bool TryGetValue(TKey key, out TValue value);
TValue GetValue(TKey key);
void SetValue(TKey key, TValue value);
KeyValuePair<TKey, TValue> GetItem(int index);
void SetItem(int index, TValue value);
}
}
Berikut implementasinya bersama dengan kelas helper:
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
namespace mattmc3.Common.Collections.Generic {
public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue> {
#region Fields/Properties
private KeyedCollection2<TKey, KeyValuePair<TKey, TValue>> _keyedCollection;
public TValue this[TKey key] {
get {
return GetValue(key);
}
set {
SetValue(key, value);
}
}
public TValue this[int index] {
get {
return GetItem(index).Value;
}
set {
SetItem(index, value);
}
}
public int Count {
get { return _keyedCollection.Count; }
}
public ICollection<TKey> Keys {
get {
return _keyedCollection.Select(x => x.Key).ToList();
}
}
public ICollection<TValue> Values {
get {
return _keyedCollection.Select(x => x.Value).ToList();
}
}
public IEqualityComparer<TKey> Comparer {
get;
private set;
}
#endregion
#region Constructors
public OrderedDictionary() {
Initialize();
}
public OrderedDictionary(IEqualityComparer<TKey> comparer) {
Initialize(comparer);
}
public OrderedDictionary(IOrderedDictionary<TKey, TValue> dictionary) {
Initialize();
foreach (KeyValuePair<TKey, TValue> pair in dictionary) {
_keyedCollection.Add(pair);
}
}
public OrderedDictionary(IOrderedDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) {
Initialize(comparer);
foreach (KeyValuePair<TKey, TValue> pair in dictionary) {
_keyedCollection.Add(pair);
}
}
#endregion
#region Methods
private void Initialize(IEqualityComparer<TKey> comparer = null) {
this.Comparer = comparer;
if (comparer != null) {
_keyedCollection = new KeyedCollection2<TKey, KeyValuePair<TKey, TValue>>(x => x.Key, comparer);
}
else {
_keyedCollection = new KeyedCollection2<TKey, KeyValuePair<TKey, TValue>>(x => x.Key);
}
}
public void Add(TKey key, TValue value) {
_keyedCollection.Add(new KeyValuePair<TKey, TValue>(key, value));
}
public void Clear() {
_keyedCollection.Clear();
}
public void Insert(int index, TKey key, TValue value) {
_keyedCollection.Insert(index, new KeyValuePair<TKey, TValue>(key, value));
}
public int IndexOf(TKey key) {
if (_keyedCollection.Contains(key)) {
return _keyedCollection.IndexOf(_keyedCollection[key]);
}
else {
return -1;
}
}
public bool ContainsValue(TValue value) {
return this.Values.Contains(value);
}
public bool ContainsValue(TValue value, IEqualityComparer<TValue> comparer) {
return this.Values.Contains(value, comparer);
}
public bool ContainsKey(TKey key) {
return _keyedCollection.Contains(key);
}
public KeyValuePair<TKey, TValue> GetItem(int index) {
if (index < 0 || index >= _keyedCollection.Count) {
throw new ArgumentException(String.Format("The index was outside the bounds of the dictionary: {0}", index));
}
return _keyedCollection[index];
}
public void SetItem(int index, TValue value) {
if (index < 0 || index >= _keyedCollection.Count) {
throw new ArgumentException("The index is outside the bounds of the dictionary: {0}".FormatWith(index));
}
var kvp = new KeyValuePair<TKey, TValue>(_keyedCollection[index].Key, value);
_keyedCollection[index] = kvp;
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
return _keyedCollection.GetEnumerator();
}
public bool Remove(TKey key) {
return _keyedCollection.Remove(key);
}
public void RemoveAt(int index) {
if (index < 0 || index >= _keyedCollection.Count) {
throw new ArgumentException(String.Format("The index was outside the bounds of the dictionary: {0}", index));
}
_keyedCollection.RemoveAt(index);
}
public TValue GetValue(TKey key) {
if (_keyedCollection.Contains(key) == false) {
throw new ArgumentException("The given key is not present in the dictionary: {0}".FormatWith(key));
}
var kvp = _keyedCollection[key];
return kvp.Value;
}
public void SetValue(TKey key, TValue value) {
var kvp = new KeyValuePair<TKey, TValue>(key, value);
var idx = IndexOf(key);
if (idx > -1) {
_keyedCollection[idx] = kvp;
}
else {
_keyedCollection.Add(kvp);
}
}
public bool TryGetValue(TKey key, out TValue value) {
if (_keyedCollection.Contains(key)) {
value = _keyedCollection[key].Value;
return true;
}
else {
value = default(TValue);
return false;
}
}
#endregion
#region sorting
public void SortKeys() {
_keyedCollection.SortByKeys();
}
public void SortKeys(IComparer<TKey> comparer) {
_keyedCollection.SortByKeys(comparer);
}
public void SortKeys(Comparison<TKey> comparison) {
_keyedCollection.SortByKeys(comparison);
}
public void SortValues() {
var comparer = Comparer<TValue>.Default;
SortValues(comparer);
}
public void SortValues(IComparer<TValue> comparer) {
_keyedCollection.Sort((x, y) => comparer.Compare(x.Value, y.Value));
}
public void SortValues(Comparison<TValue> comparison) {
_keyedCollection.Sort((x, y) => comparison(x.Value, y.Value));
}
#endregion
#region IDictionary<TKey, TValue>
void IDictionary<TKey, TValue>.Add(TKey key, TValue value) {
Add(key, value);
}
bool IDictionary<TKey, TValue>.ContainsKey(TKey key) {
return ContainsKey(key);
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys {
get { return Keys; }
}
bool IDictionary<TKey, TValue>.Remove(TKey key) {
return Remove(key);
}
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) {
return TryGetValue(key, out value);
}
ICollection<TValue> IDictionary<TKey, TValue>.Values {
get { return Values; }
}
TValue IDictionary<TKey, TValue>.this[TKey key] {
get {
return this[key];
}
set {
this[key] = value;
}
}
#endregion
#region ICollection<KeyValuePair<TKey, TValue>>
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) {
_keyedCollection.Add(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear() {
_keyedCollection.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) {
return _keyedCollection.Contains(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
_keyedCollection.CopyTo(array, arrayIndex);
}
int ICollection<KeyValuePair<TKey, TValue>>.Count {
get { return _keyedCollection.Count; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
get { return false; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) {
return _keyedCollection.Remove(item);
}
#endregion
#region IEnumerable<KeyValuePair<TKey, TValue>>
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region IEnumerable
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region IOrderedDictionary
IDictionaryEnumerator IOrderedDictionary.GetEnumerator() {
return new DictionaryEnumerator<TKey, TValue>(this);
}
void IOrderedDictionary.Insert(int index, object key, object value) {
Insert(index, (TKey)key, (TValue)value);
}
void IOrderedDictionary.RemoveAt(int index) {
RemoveAt(index);
}
object IOrderedDictionary.this[int index] {
get {
return this[index];
}
set {
this[index] = (TValue)value;
}
}
#endregion
#region IDictionary
void IDictionary.Add(object key, object value) {
Add((TKey)key, (TValue)value);
}
void IDictionary.Clear() {
Clear();
}
bool IDictionary.Contains(object key) {
return _keyedCollection.Contains((TKey)key);
}
IDictionaryEnumerator IDictionary.GetEnumerator() {
return new DictionaryEnumerator<TKey, TValue>(this);
}
bool IDictionary.IsFixedSize {
get { return false; }
}
bool IDictionary.IsReadOnly {
get { return false; }
}
ICollection IDictionary.Keys {
get { return (ICollection)this.Keys; }
}
void IDictionary.Remove(object key) {
Remove((TKey)key);
}
ICollection IDictionary.Values {
get { return (ICollection)this.Values; }
}
object IDictionary.this[object key] {
get {
return this[(TKey)key];
}
set {
this[(TKey)key] = (TValue)value;
}
}
#endregion
#region ICollection
void ICollection.CopyTo(Array array, int index) {
((ICollection)_keyedCollection).CopyTo(array, index);
}
int ICollection.Count {
get { return ((ICollection)_keyedCollection).Count; }
}
bool ICollection.IsSynchronized {
get { return ((ICollection)_keyedCollection).IsSynchronized; }
}
object ICollection.SyncRoot {
get { return ((ICollection)_keyedCollection).SyncRoot; }
}
#endregion
}
public class KeyedCollection2<TKey, TItem> : KeyedCollection<TKey, TItem> {
private const string DelegateNullExceptionMessage = "Delegate passed cannot be null";
private Func<TItem, TKey> _getKeyForItemDelegate;
public KeyedCollection2(Func<TItem, TKey> getKeyForItemDelegate)
: base() {
if (getKeyForItemDelegate == null) throw new ArgumentNullException(DelegateNullExceptionMessage);
_getKeyForItemDelegate = getKeyForItemDelegate;
}
public KeyedCollection2(Func<TItem, TKey> getKeyForItemDelegate, IEqualityComparer<TKey> comparer)
: base(comparer) {
if (getKeyForItemDelegate == null) throw new ArgumentNullException(DelegateNullExceptionMessage);
_getKeyForItemDelegate = getKeyForItemDelegate;
}
protected override TKey GetKeyForItem(TItem item) {
return _getKeyForItemDelegate(item);
}
public void SortByKeys() {
var comparer = Comparer<TKey>.Default;
SortByKeys(comparer);
}
public void SortByKeys(IComparer<TKey> keyComparer) {
var comparer = new Comparer2<TItem>((x, y) => keyComparer.Compare(GetKeyForItem(x), GetKeyForItem(y)));
Sort(comparer);
}
public void SortByKeys(Comparison<TKey> keyComparison) {
var comparer = new Comparer2<TItem>((x, y) => keyComparison(GetKeyForItem(x), GetKeyForItem(y)));
Sort(comparer);
}
public void Sort() {
var comparer = Comparer<TItem>.Default;
Sort(comparer);
}
public void Sort(Comparison<TItem> comparison) {
var newComparer = new Comparer2<TItem>((x, y) => comparison(x, y));
Sort(newComparer);
}
public void Sort(IComparer<TItem> comparer) {
List<TItem> list = base.Items as List<TItem>;
if (list != null) {
list.Sort(comparer);
}
}
}
public class Comparer2<T> : Comparer<T> {
private readonly Comparison<T> _compareFunction;
#region Constructors
public Comparer2(Comparison<T> comparison) {
if (comparison == null) throw new ArgumentNullException("comparison");
_compareFunction = comparison;
}
#endregion
public override int Compare(T arg1, T arg2) {
return _compareFunction(arg1, arg2);
}
}
public class DictionaryEnumerator<TKey, TValue> : IDictionaryEnumerator, IDisposable {
readonly IEnumerator<KeyValuePair<TKey, TValue>> impl;
public void Dispose() { impl.Dispose(); }
public DictionaryEnumerator(IDictionary<TKey, TValue> value) {
this.impl = value.GetEnumerator();
}
public void Reset() { impl.Reset(); }
public bool MoveNext() { return impl.MoveNext(); }
public DictionaryEntry Entry {
get {
var pair = impl.Current;
return new DictionaryEntry(pair.Key, pair.Value);
}
}
public object Key { get { return impl.Current.Key; } }
public object Value { get { return impl.Current.Value; } }
public object Current { get { return Entry; } }
}
}
Dan tidak ada implementasi yang akan lengkap tanpa beberapa tes (tapi tragisnya, SO tidak akan membiarkan saya memposting banyak kode dalam satu posting), jadi saya harus meninggalkan Anda untuk menulis tes Anda. Tapi, saya tinggalkan beberapa di antaranya sehingga Anda bisa mendapatkan gambaran tentang cara kerjanya:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using mattmc3.Common.Collections.Generic;
namespace mattmc3.Tests.Common.Collections.Generic {
[TestClass]
public class OrderedDictionaryTests {
private OrderedDictionary<string, string> GetAlphabetDictionary(IEqualityComparer<string> comparer = null) {
OrderedDictionary<string, string> alphabet = (comparer == null ? new OrderedDictionary<string, string>() : new OrderedDictionary<string, string>(comparer));
for (var a = Convert.ToInt32('a'); a <= Convert.ToInt32('z'); a++) {
var c = Convert.ToChar(a);
alphabet.Add(c.ToString(), c.ToString().ToUpper());
}
Assert.AreEqual(26, alphabet.Count);
return alphabet;
}
private List<KeyValuePair<string, string>> GetAlphabetList() {
var alphabet = new List<KeyValuePair<string, string>>();
for (var a = Convert.ToInt32('a'); a <= Convert.ToInt32('z'); a++) {
var c = Convert.ToChar(a);
alphabet.Add(new KeyValuePair<string, string>(c.ToString(), c.ToString().ToUpper()));
}
Assert.AreEqual(26, alphabet.Count);
return alphabet;
}
[TestMethod]
public void TestAdd() {
var od = new OrderedDictionary<string, string>();
Assert.AreEqual(0, od.Count);
Assert.AreEqual(-1, od.IndexOf("foo"));
od.Add("foo", "bar");
Assert.AreEqual(1, od.Count);
Assert.AreEqual(0, od.IndexOf("foo"));
Assert.AreEqual(od[0], "bar");
Assert.AreEqual(od["foo"], "bar");
Assert.AreEqual(od.GetItem(0).Key, "foo");
Assert.AreEqual(od.GetItem(0).Value, "bar");
}
[TestMethod]
public void TestRemove() {
var od = new OrderedDictionary<string, string>();
od.Add("foo", "bar");
Assert.AreEqual(1, od.Count);
od.Remove("foo");
Assert.AreEqual(0, od.Count);
}
[TestMethod]
public void TestRemoveAt() {
var od = new OrderedDictionary<string, string>();
od.Add("foo", "bar");
Assert.AreEqual(1, od.Count);
od.RemoveAt(0);
Assert.AreEqual(0, od.Count);
}
[TestMethod]
public void TestClear() {
var od = GetAlphabetDictionary();
Assert.AreEqual(26, od.Count);
od.Clear();
Assert.AreEqual(0, od.Count);
}
[TestMethod]
public void TestOrderIsPreserved() {
var alphabetDict = GetAlphabetDictionary();
var alphabetList = GetAlphabetList();
Assert.AreEqual(26, alphabetDict.Count);
Assert.AreEqual(26, alphabetList.Count);
var keys = alphabetDict.Keys.ToList();
var values = alphabetDict.Values.ToList();
for (var i = 0; i < 26; i++) {
var dictItem = alphabetDict.GetItem(i);
var listItem = alphabetList[i];
var key = keys[i];
var value = values[i];
Assert.AreEqual(dictItem, listItem);
Assert.AreEqual(key, listItem.Key);
Assert.AreEqual(value, listItem.Value);
}
}
[TestMethod]
public void TestTryGetValue() {
var alphabetDict = GetAlphabetDictionary();
string result = null;
Assert.IsFalse(alphabetDict.TryGetValue("abc", out result));
Assert.IsNull(result);
Assert.IsTrue(alphabetDict.TryGetValue("z", out result));
Assert.AreEqual("Z", result);
}
[TestMethod]
public void TestEnumerator() {
var alphabetDict = GetAlphabetDictionary();
var keys = alphabetDict.Keys.ToList();
Assert.AreEqual(26, keys.Count);
var i = 0;
foreach (var kvp in alphabetDict) {
var value = alphabetDict[kvp.Key];
Assert.AreEqual(kvp.Value, value);
i++;
}
}
[TestMethod]
public void TestInvalidIndex() {
var alphabetDict = GetAlphabetDictionary();
try {
var notGonnaWork = alphabetDict[100];
Assert.IsTrue(false, "Exception should have thrown");
}
catch (Exception ex) {
Assert.IsTrue(ex.Message.Contains("index is outside the bounds"));
}
}
[TestMethod]
public void TestMissingKey() {
var alphabetDict = GetAlphabetDictionary();
try {
var notGonnaWork = alphabetDict["abc"];
Assert.IsTrue(false, "Exception should have thrown");
}
catch (Exception ex) {
Assert.IsTrue(ex.Message.Contains("key is not present"));
}
}
[TestMethod]
public void TestUpdateExistingValue() {
var alphabetDict = GetAlphabetDictionary();
Assert.IsTrue(alphabetDict.ContainsKey("c"));
Assert.AreEqual(2, alphabetDict.IndexOf("c"));
Assert.AreEqual(alphabetDict[2], "C");
alphabetDict[2] = "CCC";
Assert.IsTrue(alphabetDict.ContainsKey("c"));
Assert.AreEqual(2, alphabetDict.IndexOf("c"));
Assert.AreEqual(alphabetDict[2], "CCC");
}
[TestMethod]
public void TestInsertValue() {
var alphabetDict = GetAlphabetDictionary();
Assert.IsTrue(alphabetDict.ContainsKey("c"));
Assert.AreEqual(2, alphabetDict.IndexOf("c"));
Assert.AreEqual(alphabetDict[2], "C");
Assert.AreEqual(26, alphabetDict.Count);
Assert.IsFalse(alphabetDict.ContainsValue("ABC"));
alphabetDict.Insert(2, "abc", "ABC");
Assert.IsTrue(alphabetDict.ContainsKey("c"));
Assert.AreEqual(2, alphabetDict.IndexOf("abc"));
Assert.AreEqual(alphabetDict[2], "ABC");
Assert.AreEqual(27, alphabetDict.Count);
Assert.IsTrue(alphabetDict.ContainsValue("ABC"));
}
[TestMethod]
public void TestValueComparer() {
var alphabetDict = GetAlphabetDictionary();
Assert.IsFalse(alphabetDict.ContainsValue("a"));
Assert.IsTrue(alphabetDict.ContainsValue("a", StringComparer.OrdinalIgnoreCase));
}
[TestMethod]
public void TestSortByKeys() {
var alphabetDict = GetAlphabetDictionary();
var reverseAlphabetDict = GetAlphabetDictionary();
Comparison<string> stringReverse = ((x, y) => (String.Equals(x, y) ? 0 : String.Compare(x, y) >= 1 ? -1 : 1));
reverseAlphabetDict.SortKeys(stringReverse);
for (int j = 0, k = 25; j < alphabetDict.Count; j++, k--) {
var ascValue = alphabetDict.GetItem(j);
var dscValue = reverseAlphabetDict.GetItem(k);
Assert.AreEqual(ascValue.Key, dscValue.Key);
Assert.AreEqual(ascValue.Value, dscValue.Value);
}
}
- PEMBARUAN -
Sumber untuk ini dan pustaka inti .NET hilang yang sangat berguna lainnya di sini: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs
OrderedDictionary<T>
: codeproject.com/Articles/18615/…