Advertisement
zhangsongcui

ObservableDictionary

Dec 7th, 2013
147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 14.04 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Diagnostics;
  5. using System.Linq;
  6.  
  7. namespace WpfApplication1 {
  8.     /// <summary>
  9.     /// ObservableDictionary implemented using ObservableCollection
  10.     /// </summary>
  11.     /// <remarks>O(n)</remarks>
  12.     public class ObservableDictionary<TKey, TValue> : ObservableCollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> {
  13.         #region Private Fields
  14.  
  15.         private readonly IEqualityComparer<TKey> _comparer;
  16.  
  17.         #endregion
  18.  
  19.         #region Constructors
  20.  
  21.         public ObservableDictionary(IEqualityComparer<TKey> comparer = null) {
  22.             this._comparer = comparer ?? EqualityComparer<TKey>.Default;
  23.         }
  24.  
  25.         public ObservableDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary,
  26.             IEqualityComparer<TKey> comparer = null)
  27.             : base(dictionary) {
  28.             this._comparer = comparer ?? EqualityComparer<TKey>.Default;
  29.         }
  30.  
  31.         #endregion
  32.  
  33.         #region Public Methods
  34.  
  35.         private int FindIndexOfKey(TKey key) {
  36.             for (int i = 0, count = base.Count; i < count; ++i) {
  37.                 if (_comparer.Equals(key, base[i].Key)) {
  38.                     return i;
  39.                 }
  40.             }
  41.  
  42.             return -1;
  43.         }
  44.  
  45.         public void Add(TKey key, TValue value) {
  46.             if (FindIndexOfKey(key) != -1) {
  47.                 throw new ArgumentException("Key exists: " + key.ToString());
  48.             }
  49.  
  50.             base.Add(new KeyValuePair<TKey, TValue>(key, value));
  51.         }
  52.  
  53.         public bool Remove(TKey key) {
  54.             var index = FindIndexOfKey(key);
  55.             if (index == -1) {
  56.                 return false;
  57.             }
  58.  
  59.             base.RemoveAt(index);
  60.             return true;
  61.         }
  62.  
  63.  
  64.         public bool ContainsKey(TKey key) {
  65.             return FindIndexOfKey(key) != -1;
  66.         }
  67.  
  68.         public bool TryGetValue(TKey key, out TValue value) {
  69.             var index = FindIndexOfKey(key);
  70.             if (index == -1) {
  71.                 value = default(TValue);
  72.                 return false;
  73.             }
  74.  
  75.             value = base[index].Value;
  76.             return true;
  77.         }
  78.  
  79.         #endregion
  80.  
  81.         #region Public Properties
  82.  
  83.         public TValue this[TKey key] {
  84.             get {
  85.                 var index = FindIndexOfKey(key);
  86.                 if (index == -1) {
  87.                     throw new KeyNotFoundException(key.ToString());
  88.                 }
  89.  
  90.                 return base[index].Value;
  91.             }
  92.             set {
  93.                 var index = FindIndexOfKey(key);
  94.                 if (index != -1) {
  95.                     base[index] = new KeyValuePair<TKey, TValue>(key, value);
  96.                 } else {
  97.                     base.Add(new KeyValuePair<TKey, TValue>(key, value));
  98.                 }
  99.             }
  100.         }
  101.  
  102.         IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys {
  103.             get { return new KeyCollection(this); }
  104.         }
  105.  
  106.         IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values {
  107.             get { return new ValueCollection(this); }
  108.         }
  109.  
  110.         public ICollection<TKey> Keys {
  111.             get { return new KeyCollection(this); }
  112.         }
  113.  
  114.         public ICollection<TValue> Values {
  115.             get { return new ValueCollection(this); }
  116.         }
  117.  
  118.         #endregion
  119.  
  120.         #region Internal Types
  121.  
  122.         public sealed class KeyCollection : IEnumerable<TKey>, ICollection<TKey> {
  123.             private readonly ObservableDictionary<TKey, TValue> _dictionary;
  124.  
  125.             public KeyCollection(ObservableDictionary<TKey, TValue> dictionary) {
  126.                 _dictionary = dictionary;
  127.             }
  128.  
  129.             public struct Enumerator : IEnumerator<TKey> {
  130.                 private ObservableDictionary<TKey, TValue> _dictionary;
  131.                 private int _index;
  132.  
  133.                 internal Enumerator(ObservableDictionary<TKey, TValue> dictionary) {
  134.                     this._index = -1;
  135.                     this._dictionary = dictionary;
  136.                 }
  137.  
  138.                 public TKey Current {
  139.                     get {
  140.                         Debug.Assert(_dictionary != null);
  141.                         Debug.Assert(_index >= 0 && _index < _dictionary.Count);
  142.  
  143.                         return _dictionary[_index].Key;
  144.                     }
  145.                 }
  146.  
  147.                 void IDisposable.Dispose() {
  148.                     _dictionary = null;
  149.                 }
  150.  
  151.                 object System.Collections.IEnumerator.Current {
  152.                     get { return this.Current; }
  153.                 }
  154.  
  155.                 public bool MoveNext() {
  156.                     Debug.Assert(_dictionary != null);
  157.                     Debug.Assert(_index >= -1 && _index < _dictionary.Count);
  158.  
  159.                     return ++_index != _dictionary.Count;
  160.                 }
  161.  
  162.                 public void Reset() {
  163.                     _index = 0;
  164.                 }
  165.             }
  166.  
  167.             public IEnumerator<TKey> GetEnumerator() {
  168.                 return new Enumerator(_dictionary);
  169.             }
  170.  
  171.             System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
  172.                 return this.GetEnumerator();
  173.             }
  174.  
  175.             void ICollection<TKey>.Add(TKey item) {
  176.                 throw new NotSupportedException();
  177.             }
  178.  
  179.             void ICollection<TKey>.Clear() {
  180.                 throw new NotSupportedException();
  181.             }
  182.  
  183.             void ICollection<TKey>.CopyTo(TKey[] array, int arrayIndex) {
  184.                 for (int index = 0, num = _dictionary.Count; index < num; ++index) {
  185.                     array[arrayIndex++] = _dictionary[index].Key;
  186.                 }
  187.             }
  188.  
  189.             bool ICollection<TKey>.IsReadOnly {
  190.                 get { return true; }
  191.             }
  192.  
  193.  
  194.             public bool Contains(TKey item) {
  195.                 return _dictionary.ContainsKey(item);
  196.             }
  197.  
  198.             public int Count {
  199.                 get { return _dictionary.Count; }
  200.             }
  201.  
  202.             bool ICollection<TKey>.Remove(TKey item) {
  203.                 throw new NotSupportedException();
  204.             }
  205.         }
  206.         public sealed class ValueCollection : IEnumerable<TValue>, ICollection<TValue> {
  207.             private readonly ObservableDictionary<TKey, TValue> _dictionary;
  208.  
  209.             public ValueCollection(ObservableDictionary<TKey, TValue> dictionary) {
  210.                 _dictionary = dictionary;
  211.             }
  212.  
  213.             public struct Enumerator : IEnumerator<TValue> {
  214.                 private ObservableDictionary<TKey, TValue> _dictionary;
  215.                 private int _index;
  216.  
  217.                 internal Enumerator(ObservableDictionary<TKey, TValue> dictionary) {
  218.                     this._index = -1;
  219.                     this._dictionary = dictionary;
  220.                 }
  221.  
  222.                 public TValue Current {
  223.                     get {
  224.                         Debug.Assert(_dictionary != null);
  225.                         Debug.Assert(_index >= 0 && _index < _dictionary.Count);
  226.  
  227.                         return _dictionary[_index].Value;
  228.                     }
  229.                 }
  230.  
  231.                 void IDisposable.Dispose() {
  232.                     _dictionary = null;
  233.                 }
  234.  
  235.                 object System.Collections.IEnumerator.Current {
  236.                     get { return this.Current; }
  237.                 }
  238.  
  239.                 public bool MoveNext() {
  240.                     Debug.Assert(_dictionary != null);
  241.                     Debug.Assert(_index >= -1 && _index < _dictionary.Count);
  242.  
  243.                     return ++_index != _dictionary.Count;
  244.                 }
  245.  
  246.                 public void Reset() {
  247.                     _index = 0;
  248.                 }
  249.             }
  250.  
  251.             public IEnumerator<TValue> GetEnumerator() {
  252.                 return new Enumerator(_dictionary);
  253.             }
  254.  
  255.             System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
  256.                 return this.GetEnumerator();
  257.             }
  258.  
  259.             void ICollection<TValue>.Add(TValue item) {
  260.                 throw new NotSupportedException();
  261.             }
  262.  
  263.             void ICollection<TValue>.Clear() {
  264.                 throw new NotSupportedException();
  265.             }
  266.  
  267.             void ICollection<TValue>.CopyTo(TValue[] array, int arrayIndex) {
  268.                 for (int index = 0, num = _dictionary.Count; index < num; ++index) {
  269.                     array[arrayIndex++] = _dictionary[index].Value;
  270.                 }
  271.             }
  272.  
  273.             bool ICollection<TValue>.IsReadOnly {
  274.                 get { return true; }
  275.             }
  276.  
  277.  
  278.             public bool Contains(TValue item) {
  279.                 return _dictionary.Any(pair => pair.Value.Equals(item));
  280.             }
  281.  
  282.             public int Count {
  283.                 get { return _dictionary.Count; }
  284.             }
  285.  
  286.             bool ICollection<TValue>.Remove(TValue item) {
  287.                 throw new NotSupportedException();
  288.             }
  289.         }
  290.  
  291.         #endregion
  292.     }
  293.  
  294. }
  295.  
  296. using System;
  297. using System.Collections.Generic;
  298. using System.Collections.Specialized;
  299. using System.Linq;
  300.  
  301. using NUnit.Framework;
  302.  
  303. using WpfApplication1;
  304. using WpfApplication1.Model;
  305.  
  306. namespace ClassLibrary1
  307. {
  308.     [TestFixture]
  309.     public class ObservableDictionaryTester {
  310.         private ObservableDictionary<Profile, bool> _typingIndicators;
  311.         private NotifyCollectionChangedEventArgs _lastEvent;
  312.  
  313.         [SetUp]
  314.         public void Init() {
  315.             _typingIndicators = new ObservableDictionary<Profile, bool> {
  316.                 new KeyValuePair<Profile, bool>(new Profile { Id = 1, FullName = "123" }, true),
  317.                 new KeyValuePair<Profile, bool>(new Profile { Id = 2, FullName = "234" }, false),
  318.             };
  319.             _lastEvent = null;
  320.  
  321.             _typingIndicators.CollectionChanged += OnCollectionChanged;
  322.         }
  323.  
  324.         [TearDown]
  325.         public void Cleanup() {
  326.             _typingIndicators.CollectionChanged -= OnCollectionChanged;
  327.         }
  328.  
  329.         private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
  330.             _lastEvent = e;
  331.         }
  332.  
  333.         [Test]
  334.         public void TestAdd() {
  335.             _typingIndicators.Add(new Profile { Id = 3, FullName = "345" }, false);
  336.             Assert.AreEqual(3, _typingIndicators.Count);
  337.             Assert.AreEqual("345", _typingIndicators[2].Key.FullName);
  338.             Assert.AreEqual(NotifyCollectionChangedAction.Add, _lastEvent.Action);
  339.             Assert.AreEqual(_typingIndicators[2], _lastEvent.NewItems[0]);
  340.  
  341.             _lastEvent = null;
  342.  
  343.             Assert.Throws<ArgumentException>(delegate {
  344.                 _typingIndicators.Add(new Profile { Id = 3, FullName = "345" }, true);
  345.             });
  346.             Assert.AreEqual(3, _typingIndicators.Count);
  347.             Assert.Null(_lastEvent);
  348.         }
  349.  
  350.         [Test]
  351.         public void TestRemove() {
  352.             Assert.True(_typingIndicators.Remove(new Profile { Id = 2 }));
  353.             Assert.AreEqual(1, _typingIndicators.Count);
  354.             Assert.AreEqual(NotifyCollectionChangedAction.Remove, _lastEvent.Action);
  355.             Assert.AreEqual(2, ((KeyValuePair<Profile, bool>) _lastEvent.OldItems[0]).Key.Id);
  356.  
  357.             _lastEvent = null;
  358.  
  359.             Assert.False(_typingIndicators.Remove(new Profile { Id = 2 }));
  360.             Assert.AreEqual(1, _typingIndicators.Count);
  361.             Assert.Null(_lastEvent);
  362.         }
  363.  
  364.         [Test]
  365.         public void TestUpdate() {
  366.             _typingIndicators[new Profile { Id = 2 }] = true;
  367.             Assert.AreEqual(2, _typingIndicators.Count);
  368.             Assert.True(_typingIndicators[1].Value);
  369.             Assert.AreEqual(NotifyCollectionChangedAction.Replace, _lastEvent.Action);
  370.             Assert.False(((KeyValuePair<Profile, bool>)_lastEvent.OldItems[0]).Value);
  371.             Assert.True(((KeyValuePair<Profile, bool>)_lastEvent.NewItems[0]).Value);
  372.  
  373.             _lastEvent = null;
  374.  
  375.             _typingIndicators[new Profile { Id = 3 }] = false;
  376.             Assert.AreEqual(3, _typingIndicators.Count);
  377.             Assert.False(_typingIndicators[2].Value);
  378.             Assert.NotNull(_lastEvent);
  379.             Assert.AreEqual(NotifyCollectionChangedAction.Add, _lastEvent.Action);
  380.             Assert.AreEqual(3, ((KeyValuePair<Profile, bool>)_lastEvent.NewItems[0]).Key.Id);
  381.             Assert.False(((KeyValuePair<Profile, bool>)_lastEvent.NewItems[0]).Value);
  382.         }
  383.  
  384.         [Test]
  385.         public void TestGet() {
  386.             Assert.False(_typingIndicators[new Profile { Id = 2 }]);
  387.  
  388.             bool value;
  389.  
  390.             Assert.Throws<KeyNotFoundException>(delegate {
  391.                 value = _typingIndicators[new Profile { Id = 3 }];
  392.             });
  393.  
  394.             Assert.True(_typingIndicators.TryGetValue(new Profile { Id = 1 }, out value));
  395.             Assert.True(value);
  396.  
  397.             Assert.False(_typingIndicators.TryGetValue(new Profile { Id = 4 }, out value));
  398.             Assert.False(value);
  399.         }
  400.  
  401.         [Test]
  402.         public void TestContainsKey() {
  403.             Assert.True(_typingIndicators.ContainsKey(new Profile { Id = 2 }));
  404.             Assert.False(_typingIndicators.ContainsKey(new Profile { Id = 4 }));
  405.         }
  406.  
  407.         [Test]
  408.         public void TestGetKeys() {
  409.             Assert.AreEqual(2, _typingIndicators.Keys.Count);
  410.  
  411.             Assert.True(_typingIndicators.Keys.SequenceEqual(new[] {
  412.                 new Profile { Id = 1 },
  413.                 new Profile { Id = 2 },
  414.             }));
  415.  
  416.             Assert.True(_typingIndicators.Keys.Select(profile => profile.FullName).SequenceEqual(new[] {
  417.                 "123",
  418.                 "234",
  419.             }));
  420.         }
  421.  
  422.         [Test]
  423.         public void TestGetValues() {
  424.             Assert.AreEqual(2, _typingIndicators.Values.Count);
  425.             Assert.True(_typingIndicators.Values.SequenceEqual(new[] {
  426.                 true,
  427.                 false,
  428.             }));
  429.         }
  430.     }
  431. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement