Added new class ObservableDictionary to the collection class library; 100% test coverage for the new ObservableDictionary class; fixed some minor issues with the code of ObservableCollection and ReadOnlyDictionary
git-svn-id: file:///srv/devel/repo-conversion/nusu@117 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
f0498fbb60
commit
b90ff1c78b
|
@ -77,6 +77,10 @@
|
||||||
<Compile Include="Source\Collections\ObservableCollection.Test.cs">
|
<Compile Include="Source\Collections\ObservableCollection.Test.cs">
|
||||||
<DependentUpon>ObservableCollection.cs</DependentUpon>
|
<DependentUpon>ObservableCollection.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\Collections\ObservableDictionary.cs" />
|
||||||
|
<Compile Include="Source\Collections\ObservableDictionary.Test.cs">
|
||||||
|
<DependentUpon>ObservableDictionary.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Collections\PairPriorityQueue.cs" />
|
<Compile Include="Source\Collections\PairPriorityQueue.cs" />
|
||||||
<Compile Include="Source\Collections\PairPriorityQueue.Test.cs">
|
<Compile Include="Source\Collections\PairPriorityQueue.Test.cs">
|
||||||
<DependentUpon>PairPriorityQueue.cs</DependentUpon>
|
<DependentUpon>PairPriorityQueue.cs</DependentUpon>
|
||||||
|
|
|
@ -59,6 +59,10 @@
|
||||||
<Compile Include="Source\Collections\ObservableCollection.Test.cs">
|
<Compile Include="Source\Collections\ObservableCollection.Test.cs">
|
||||||
<DependentUpon>ObservableCollection.cs</DependentUpon>
|
<DependentUpon>ObservableCollection.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\Collections\ObservableDictionary.cs" />
|
||||||
|
<Compile Include="Source\Collections\ObservableDictionary.Test.cs">
|
||||||
|
<DependentUpon>ObservableDictionary.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Collections\PairPriorityQueue.cs" />
|
<Compile Include="Source\Collections\PairPriorityQueue.cs" />
|
||||||
<Compile Include="Source\Collections\PairPriorityQueue.Test.cs">
|
<Compile Include="Source\Collections\PairPriorityQueue.Test.cs">
|
||||||
<DependentUpon>PairPriorityQueue.cs</DependentUpon>
|
<DependentUpon>PairPriorityQueue.cs</DependentUpon>
|
||||||
|
|
|
@ -34,23 +34,23 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
#region interface IObservableCollectionSubscriber
|
#region interface IObservableCollectionSubscriber
|
||||||
|
|
||||||
/// <summary>Interface used to test the observable collection.</summary>
|
/// <summary>Interface used to test the observable collection</summary>
|
||||||
public interface IObservableCollectionSubscriber {
|
public interface IObservableCollectionSubscriber {
|
||||||
|
|
||||||
/// <summary>Called when the collection is about to clear its contents</summary>
|
/// <summary>Called when the collection is about to clear its contents</summary>
|
||||||
/// <param name="sender">Collection that is clearing its contents</param>
|
/// <param name="sender">Collection that is clearing its contents</param>
|
||||||
/// <param name="e">Not used</param>
|
/// <param name="arguments">Not used</param>
|
||||||
void Clearing(object sender, EventArgs e);
|
void Clearing(object sender, EventArgs arguments);
|
||||||
|
|
||||||
/// <summary>Called when an item is added to the collection</summary>
|
/// <summary>Called when an item is added to the collection</summary>
|
||||||
/// <param name="sender">Collection to which an item is being added</param>
|
/// <param name="sender">Collection to which an item is being added</param>
|
||||||
/// <param name="e">Contains the item that is being added</param>
|
/// <param name="arguments">Contains the item that is being added</param>
|
||||||
void ItemAdded(object sender, ItemEventArgs<int> e);
|
void ItemAdded(object sender, ItemEventArgs<int> arguments);
|
||||||
|
|
||||||
/// <summary>Called when an item is removed from the collection</summary>
|
/// <summary>Called when an item is removed from the collection</summary>
|
||||||
/// <param name="sender">Collection from which an item is being removed</param>
|
/// <param name="sender">Collection from which an item is being removed</param>
|
||||||
/// <param name="e">Contains the item that is being removed</param>
|
/// <param name="arguments">Contains the item that is being removed</param>
|
||||||
void ItemRemoved(object sender, ItemEventArgs<int> e);
|
void ItemRemoved(object sender, ItemEventArgs<int> arguments);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +73,6 @@ namespace Nuclex.Support.Collections {
|
||||||
new EventHandler<ItemEventArgs<int>>(
|
new EventHandler<ItemEventArgs<int>>(
|
||||||
this.mockedSubscriber.ItemRemoved
|
this.mockedSubscriber.ItemRemoved
|
||||||
);
|
);
|
||||||
|
|
||||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Tests whether the Clearing event is fired</summary>
|
/// <summary>Tests whether the Clearing event is fired</summary>
|
||||||
|
|
539
Source/Collections/ObservableDictionary.Test.cs
Normal file
539
Source/Collections/ObservableDictionary.Test.cs
Normal file
|
@ -0,0 +1,539 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2009 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#if UNITTEST
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NMock2;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
/// <summary>Unit Test for the observable dictionary wrapper</summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class ObservableDictionaryTest {
|
||||||
|
|
||||||
|
#region interface IObservableDictionarySubscriber
|
||||||
|
|
||||||
|
/// <summary>Interface used to test the observable dictionary</summary>
|
||||||
|
public interface IObservableDictionarySubscriber {
|
||||||
|
|
||||||
|
/// <summary>Called when the dictionary is about to clear its contents</summary>
|
||||||
|
/// <param name="sender">Dictionary that is clearing its contents</param>
|
||||||
|
/// <param name="arguments">Not used</param>
|
||||||
|
void Clearing(object sender, EventArgs arguments);
|
||||||
|
|
||||||
|
/// <summary>Called when an item is added to the dictionary</summary>
|
||||||
|
/// <param name="sender">Dictionary to which an item is being added</param>
|
||||||
|
/// <param name="arguments">Contains the item that is being added</param>
|
||||||
|
void ItemAdded(object sender, ItemEventArgs<KeyValuePair<int, string>> arguments);
|
||||||
|
|
||||||
|
/// <summary>Called when an item is removed from the dictionary</summary>
|
||||||
|
/// <param name="sender">Dictionary from which an item is being removed</param>
|
||||||
|
/// <param name="arguments">Contains the item that is being removed</param>
|
||||||
|
void ItemRemoved(object sender, ItemEventArgs<KeyValuePair<int, string>> arguments);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // interface IObservableDictionarySubscriber
|
||||||
|
|
||||||
|
/// <summary>Initialization routine executed before each test is run</summary>
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() {
|
||||||
|
this.mockery = new Mockery();
|
||||||
|
|
||||||
|
this.mockedSubscriber = this.mockery.NewMock<IObservableDictionarySubscriber>();
|
||||||
|
|
||||||
|
this.observedDictionary = new ObservableDictionary<int, string>();
|
||||||
|
this.observedDictionary.Add(1, "one");
|
||||||
|
this.observedDictionary.Add(2, "two");
|
||||||
|
this.observedDictionary.Add(3, "three");
|
||||||
|
this.observedDictionary.Add(42, "forty-two");
|
||||||
|
|
||||||
|
this.observedDictionary.Clearing += new EventHandler(this.mockedSubscriber.Clearing);
|
||||||
|
this.observedDictionary.ItemAdded +=
|
||||||
|
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
|
||||||
|
this.mockedSubscriber.ItemAdded
|
||||||
|
);
|
||||||
|
this.observedDictionary.ItemRemoved +=
|
||||||
|
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
|
||||||
|
this.mockedSubscriber.ItemRemoved
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the default constructor of the observable dictionary works
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestDefaultConstructor() {
|
||||||
|
ObservableDictionary<int, string> testDictionary =
|
||||||
|
new ObservableDictionary<int, string>();
|
||||||
|
|
||||||
|
Assert.AreEqual(0, testDictionary.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the copy constructor of the observable dictionary works
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCopyConstructor() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(numbers, testDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that the IsReadOnly property is working</summary>
|
||||||
|
[Test]
|
||||||
|
public void TestIsReadOnly() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
Assert.IsFalse(testDictionary.IsReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Contains() method of the observable dictionary is able to
|
||||||
|
/// determine if the dictionary contains an item
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestContains() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
Assert.IsTrue(
|
||||||
|
testDictionary.Contains(new KeyValuePair<int, string>(42, "forty-two"))
|
||||||
|
);
|
||||||
|
Assert.IsFalse(
|
||||||
|
testDictionary.Contains(new KeyValuePair<int, string>(24, "twenty-four"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Contains() method of the observable dictionary is able to
|
||||||
|
/// determine if the dictionary contains a key
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestContainsKey() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
Assert.IsTrue(testDictionary.ContainsKey(42));
|
||||||
|
Assert.IsFalse(testDictionary.ContainsKey(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the CopyTo() of the observable dictionary works
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCopyToArray() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
|
||||||
|
|
||||||
|
testDictionary.CopyTo(items, 0);
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(numbers, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the typesafe enumerator of the observable dictionary is working
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestTypesafeEnumerator() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
List<KeyValuePair<int, string>> outputItems = new List<KeyValuePair<int, string>>();
|
||||||
|
foreach(KeyValuePair<int, string> item in testDictionary) {
|
||||||
|
outputItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(numbers, outputItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the keys collection of the observable dictionary can be queried
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestGetKeysCollection() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
ICollection<int> inputKeys = numbers.Keys;
|
||||||
|
ICollection<int> keys = testDictionary.Keys;
|
||||||
|
CollectionAssert.AreEquivalent(inputKeys, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the values collection of the observable dictionary can be queried
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestGetValuesCollection() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
ICollection<string> inputValues = numbers.Values;
|
||||||
|
ICollection<string> values = testDictionary.Values;
|
||||||
|
CollectionAssert.AreEquivalent(inputValues, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the TryGetValue() method of the observable dictionary is working
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestTryGetValue() {
|
||||||
|
string value;
|
||||||
|
|
||||||
|
Assert.IsTrue(this.observedDictionary.TryGetValue(42, out value));
|
||||||
|
Assert.AreEqual("forty-two", value);
|
||||||
|
|
||||||
|
Assert.IsFalse(this.observedDictionary.TryGetValue(24, out value));
|
||||||
|
Assert.AreEqual(null, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the retrieval of values using the indexer of the observable
|
||||||
|
/// dictionary is working
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRetrieveValueByIndexer() {
|
||||||
|
Assert.AreEqual("forty-two", this.observedDictionary[42]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether an exception is thrown if the indexer of the observable dictionary
|
||||||
|
/// is used to attempt to retrieve a non-existing value
|
||||||
|
/// </summary>
|
||||||
|
[Test, ExpectedException(typeof(KeyNotFoundException))]
|
||||||
|
public void TestThrowOnRetrieveNonExistingValueByIndexer() {
|
||||||
|
string numberName = this.observedDictionary[24];
|
||||||
|
Console.WriteLine(numberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Add() methods works via the generic
|
||||||
|
/// IDictionary<> interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestAddViaGenericIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary<int, string>).Add(10, "ten");
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
CollectionAssert.Contains(
|
||||||
|
this.observedDictionary, new KeyValuePair<int, string>(10, "ten")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Remove() method works via the generic
|
||||||
|
/// IDictionary<> interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveViaGenericIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary<int, string>).Remove(3);
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
CollectionAssert.DoesNotContain(this.observedDictionary.Keys, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the TryGetValue() method of the observable dictionary is working
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRetrieveValueByIndexerViaGenericIDictionary() {
|
||||||
|
Assert.AreEqual(
|
||||||
|
"forty-two", (this.observedDictionary as IDictionary<int, string>)[42]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the indexer can be used to insert an item via the generic
|
||||||
|
/// IDictionar<> interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestReplaceByIndexerViaGenericIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary<int, string>)[42] = "two and fourty";
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Clear() method of observable dictionary is working
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestClearViaIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary).Clear();
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
Assert.AreEqual(0, this.observedDictionary.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Add() method works via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestAddViaIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary).Add(24, "twenty-four");
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
CollectionAssert.Contains(
|
||||||
|
this.observedDictionary, new KeyValuePair<int, string>(24, "twenty-four")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Contains() method of the observable dictionary is able to
|
||||||
|
/// determine if the dictionary contains an item via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestContainsViaIDictionary() {
|
||||||
|
Assert.IsTrue((this.observedDictionary as IDictionary).Contains(42));
|
||||||
|
Assert.IsFalse((this.observedDictionary as IDictionary).Contains(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the GetEnumerator() method of the observable dictionary
|
||||||
|
/// returns a working enumerator if accessed via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestEnumeratorViaIDictionary() {
|
||||||
|
Dictionary<int, string> outputNumbers = new Dictionary<int, string>();
|
||||||
|
foreach(DictionaryEntry entry in (this.observedDictionary as IDictionary)) {
|
||||||
|
(outputNumbers as IDictionary).Add(entry.Key, entry.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
CollectionAssert.AreEquivalent(this.observedDictionary, outputNumbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the IsFixedSize property of the observable dictionary returns
|
||||||
|
/// the expected result for a read only dictionary based on a dynamic dictionary
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestIsFixedSizeViaIList() {
|
||||||
|
Assert.IsFalse((this.observedDictionary as IDictionary).IsFixedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the keys collection of the observable dictionary can be queried
|
||||||
|
/// via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestGetKeysCollectionViaIDictionary() {
|
||||||
|
ICollection keys = (this.observedDictionary as IDictionary).Keys;
|
||||||
|
Assert.AreEqual(this.observedDictionary.Count, keys.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the values collection of the observable dictionary can be queried
|
||||||
|
/// via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestGetValuesCollectionViaIDictionary() {
|
||||||
|
ICollection values = (this.observedDictionary as IDictionary).Values;
|
||||||
|
Assert.AreEqual(this.observedDictionary.Count, values.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether Remove() method works via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveViaIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary).Remove(3);
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
CollectionAssert.DoesNotContain(this.observedDictionary.Keys, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the retrieval of values using the indexer of the observable
|
||||||
|
/// dictionary is working via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRetrieveValueByIndexerViaIDictionary() {
|
||||||
|
Assert.AreEqual("forty-two", (this.observedDictionary as IDictionary)[42]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies the indexer can be used to insert an item via the IDictionary interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestReplaceByIndexerViaIDictionary() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
|
||||||
|
(this.observedDictionary as IDictionary)[42] = "two and fourty";
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether Add() method is working via the generic
|
||||||
|
/// ICollection<> interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestAddViaGenericICollection() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
|
||||||
|
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Add(
|
||||||
|
new KeyValuePair<int, string>(24, "twenty-four")
|
||||||
|
);
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
CollectionAssert.Contains(
|
||||||
|
this.observedDictionary, new KeyValuePair<int, string>(24, "twenty-four")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Clear() method is working via the generic
|
||||||
|
/// ICollection<> interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestClearViaGenericICollection() {
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments();
|
||||||
|
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Clear();
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
Assert.AreEqual(0, this.observedDictionary.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the Remove() method is working via the
|
||||||
|
/// generic ICollection<> interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveViaGenericICollection() {
|
||||||
|
IEnumerator<KeyValuePair<int, string>> enumerator =
|
||||||
|
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).GetEnumerator();
|
||||||
|
enumerator.MoveNext();
|
||||||
|
Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
|
||||||
|
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Remove(
|
||||||
|
enumerator.Current
|
||||||
|
);
|
||||||
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
CollectionAssert.DoesNotContain(this.observedDictionary, enumerator.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the CopyTo() of the observable dictionary works when called
|
||||||
|
/// via the the ICollection interface
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCopyToArrayViaICollection() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
DictionaryEntry[] entries = new DictionaryEntry[numbers.Count];
|
||||||
|
(testDictionary as ICollection).CopyTo(entries, 0);
|
||||||
|
|
||||||
|
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
|
||||||
|
for(int index = 0; index < entries.Length; ++index) {
|
||||||
|
items[index] = new KeyValuePair<int, string>(
|
||||||
|
(int)entries[index].Key, (string)entries[index].Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
CollectionAssert.AreEquivalent(numbers, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestSynchronization() {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||||
|
|
||||||
|
if(!(testDictionary as ICollection).IsSynchronized) {
|
||||||
|
lock((testDictionary as ICollection).SyncRoot) {
|
||||||
|
Assert.AreEqual(numbers.Count, testDictionary.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test whether the observable dictionary can be serialized
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestSerialization() {
|
||||||
|
BinaryFormatter formatter = new BinaryFormatter();
|
||||||
|
|
||||||
|
using(MemoryStream memory = new MemoryStream()) {
|
||||||
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
|
ObservableDictionary<int, string> testDictionary1 = makeObservable(numbers);
|
||||||
|
|
||||||
|
formatter.Serialize(memory, testDictionary1);
|
||||||
|
memory.Position = 0;
|
||||||
|
object testDictionary2 = formatter.Deserialize(memory);
|
||||||
|
|
||||||
|
CollectionAssert.AreEquivalent(testDictionary1, (IEnumerable)testDictionary2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new observable dictionary filled with some values for testing
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The newly created observable dictionary</returns>
|
||||||
|
private static Dictionary<int, string> createTestDictionary() {
|
||||||
|
Dictionary<int, string> numbers = new Dictionary<int, string>();
|
||||||
|
numbers.Add(1, "one");
|
||||||
|
numbers.Add(2, "two");
|
||||||
|
numbers.Add(3, "three");
|
||||||
|
numbers.Add(42, "forty-two");
|
||||||
|
return new Dictionary<int, string>(numbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new observable dictionary filled with some values for testing
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The newly created observable dictionary</returns>
|
||||||
|
private static ObservableDictionary<int, string> makeObservable(
|
||||||
|
IDictionary<int, string> dictionary
|
||||||
|
) {
|
||||||
|
return new ObservableDictionary<int, string>(dictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Mock object factory</summary>
|
||||||
|
private Mockery mockery;
|
||||||
|
/// <summary>The mocked observable collection subscriber</summary>
|
||||||
|
private IObservableDictionarySubscriber mockedSubscriber;
|
||||||
|
/// <summary>An observable dictionary to which a mock will be subscribed</summary>
|
||||||
|
private ObservableDictionary<int, string> observedDictionary;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Collections
|
||||||
|
|
||||||
|
#endif // UNITTEST
|
405
Source/Collections/ObservableDictionary.cs
Normal file
405
Source/Collections/ObservableDictionary.cs
Normal file
|
@ -0,0 +1,405 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2009 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
/// <summary>A dictionary that sneds out change notifications</summary>
|
||||||
|
/// <typeparam name="KeyType">Type of the keys used in the dictionary</typeparam>
|
||||||
|
/// <typeparam name="ValueType">Type of the values used in the dictionary</typeparam>
|
||||||
|
[Serializable]
|
||||||
|
public class ObservableDictionary<KeyType, ValueType> :
|
||||||
|
#if !COMPACTFRAMEWORK
|
||||||
|
ISerializable,
|
||||||
|
IDeserializationCallback,
|
||||||
|
#endif
|
||||||
|
IDictionary<KeyType, ValueType>,
|
||||||
|
IDictionary {
|
||||||
|
|
||||||
|
#if !COMPACTFRAMEWORK
|
||||||
|
#region class SerializedDictionary
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary wrapped used to reconstruct a serialized read only dictionary
|
||||||
|
/// </summary>
|
||||||
|
private class SerializedDictionary : Dictionary<KeyType, ValueType> {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the System.WeakReference class, using deserialized
|
||||||
|
/// data from the specified serialization and stream objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">
|
||||||
|
/// An object that holds all the data needed to serialize or deserialize the
|
||||||
|
/// current System.WeakReference object.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="context">
|
||||||
|
/// (Reserved) Describes the source and destination of the serialized stream
|
||||||
|
/// specified by info.
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="System.ArgumentNullException">
|
||||||
|
/// The info parameter is null.
|
||||||
|
/// </exception>
|
||||||
|
public SerializedDictionary(SerializationInfo info, StreamingContext context) :
|
||||||
|
base(info, context) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // class SerializeDictionary
|
||||||
|
#endif // !COMPACTFRAMEWORK
|
||||||
|
|
||||||
|
/// <summary>Raised when an item has been added to the dictionary</summary>
|
||||||
|
public event EventHandler<ItemEventArgs<KeyValuePair<KeyType, ValueType>>> ItemAdded;
|
||||||
|
/// <summary>Raised when an item is removed from the dictionary</summary>
|
||||||
|
public event EventHandler<ItemEventArgs<KeyValuePair<KeyType, ValueType>>> ItemRemoved;
|
||||||
|
/// <summary>Raised when the dictionary is about to be cleared</summary>
|
||||||
|
public event EventHandler Clearing;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new observable dictionary</summary>
|
||||||
|
public ObservableDictionary() : this(new Dictionary<KeyType, ValueType>()) { }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new observable Dictionary wrapper</summary>
|
||||||
|
/// <param name="dictionary">Dictionary that will be wrapped</param>
|
||||||
|
public ObservableDictionary(IDictionary<KeyType, ValueType> dictionary) {
|
||||||
|
this.typedDictionary = dictionary;
|
||||||
|
this.objectDictionary = (this.typedDictionary as IDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !COMPACTFRAMEWORK
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the System.WeakReference class, using deserialized
|
||||||
|
/// data from the specified serialization and stream objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">
|
||||||
|
/// An object that holds all the data needed to serialize or deserialize the
|
||||||
|
/// current System.WeakReference object.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="context">
|
||||||
|
/// (Reserved) Describes the source and destination of the serialized stream
|
||||||
|
/// specified by info.
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="System.ArgumentNullException">
|
||||||
|
/// The info parameter is null.
|
||||||
|
/// </exception>
|
||||||
|
protected ObservableDictionary(SerializationInfo info, StreamingContext context) :
|
||||||
|
this(new SerializedDictionary(info, context)) { }
|
||||||
|
#endif // !COMPACTFRAMEWORK
|
||||||
|
|
||||||
|
/// <summary>Whether the directory is write-protected</summary>
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return this.typedDictionary.IsReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified KeyValuePair is contained in the Dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">KeyValuePair that will be checked for</param>
|
||||||
|
/// <returns>True if the provided KeyValuePair was contained in the Dictionary</returns>
|
||||||
|
public bool Contains(KeyValuePair<KeyType, ValueType> item) {
|
||||||
|
return this.typedDictionary.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the Dictionary contains the specified key</summary>
|
||||||
|
/// <param name="key">Key that will be checked for</param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if an entry with the specified key was contained in the Dictionary
|
||||||
|
/// </returns>
|
||||||
|
public bool ContainsKey(KeyType key) {
|
||||||
|
return this.typedDictionary.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Copies the contents of the Dictionary into an array</summary>
|
||||||
|
/// <param name="array">Array the Dictionary will be copied into</param>
|
||||||
|
/// <param name="arrayIndex">
|
||||||
|
/// Starting index at which to begin filling the destination array
|
||||||
|
/// </param>
|
||||||
|
public void CopyTo(KeyValuePair<KeyType, ValueType>[] array, int arrayIndex) {
|
||||||
|
this.typedDictionary.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Number of elements contained in the Dictionary</summary>
|
||||||
|
public int Count {
|
||||||
|
get { return this.typedDictionary.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a new enumerator for the Dictionary</summary>
|
||||||
|
/// <returns>The new Dictionary enumerator</returns>
|
||||||
|
public IEnumerator<KeyValuePair<KeyType, ValueType>> GetEnumerator() {
|
||||||
|
return this.typedDictionary.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Collection of all keys contained in the Dictionary</summary>
|
||||||
|
public ICollection<KeyType> Keys {
|
||||||
|
get { return this.typedDictionary.Keys; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Collection of all values contained in the Dictionary</summary>
|
||||||
|
public ICollection<ValueType> Values {
|
||||||
|
get { return this.typedDictionary.Values; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to retrieve the item with the specified key from the Dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key of the item to attempt to retrieve</param>
|
||||||
|
/// <param name="value">
|
||||||
|
/// Output parameter that will receive the key upon successful completion
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if the item was found and has been placed in the output parameter
|
||||||
|
/// </returns>
|
||||||
|
public bool TryGetValue(KeyType key, out ValueType value) {
|
||||||
|
return this.typedDictionary.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Accesses an item in the Dictionary by its key</summary>
|
||||||
|
/// <param name="key">Key of the item that will be accessed</param>
|
||||||
|
public ValueType this[KeyType key] {
|
||||||
|
get { return this.typedDictionary[key]; }
|
||||||
|
set {
|
||||||
|
bool removed;
|
||||||
|
ValueType oldValue;
|
||||||
|
removed = this.typedDictionary.TryGetValue(key, out oldValue);
|
||||||
|
|
||||||
|
this.typedDictionary[key] = value;
|
||||||
|
|
||||||
|
if(removed) {
|
||||||
|
OnRemoved(new KeyValuePair<KeyType, ValueType>(key, oldValue));
|
||||||
|
}
|
||||||
|
OnAdded(new KeyValuePair<KeyType, ValueType>(key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Inserts an item into the Dictionary</summary>
|
||||||
|
/// <param name="key">Key under which to add the new item</param>
|
||||||
|
/// <param name="value">Item that will be added to the Dictionary</param>
|
||||||
|
public void Add(KeyType key, ValueType value) {
|
||||||
|
this.typedDictionary.Add(key, value);
|
||||||
|
OnAdded(new KeyValuePair<KeyType, ValueType>(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes the item with the specified key from the Dictionary</summary>
|
||||||
|
/// <param name="key">Key of the elementes that will be removed</param>
|
||||||
|
/// <returns>True if an item with the specified key was found and removed</returns>
|
||||||
|
public bool Remove(KeyType key) {
|
||||||
|
ValueType oldValue;
|
||||||
|
this.typedDictionary.TryGetValue(key, out oldValue);
|
||||||
|
|
||||||
|
bool removed = this.typedDictionary.Remove(key);
|
||||||
|
if(removed) {
|
||||||
|
OnRemoved(new KeyValuePair<KeyType, ValueType>(key, oldValue));
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the Dictionary</summary>
|
||||||
|
public void Clear() {
|
||||||
|
OnClearing();
|
||||||
|
this.typedDictionary.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fires the 'ItemAdded' event</summary>
|
||||||
|
/// <param name="item">Item that has been added to the collection</param>
|
||||||
|
protected virtual void OnAdded(KeyValuePair<KeyType, ValueType> item) {
|
||||||
|
if(ItemAdded != null)
|
||||||
|
ItemAdded(this, new ItemEventArgs<KeyValuePair<KeyType, ValueType>>(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fires the 'ItemRemoved' event</summary>
|
||||||
|
/// <param name="item">Item that has been removed from the collection</param>
|
||||||
|
protected virtual void OnRemoved(KeyValuePair<KeyType, ValueType> item) {
|
||||||
|
if(ItemRemoved != null)
|
||||||
|
ItemRemoved(this, new ItemEventArgs<KeyValuePair<KeyType, ValueType>>(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fires the 'Clearing' event</summary>
|
||||||
|
protected virtual void OnClearing() {
|
||||||
|
if(Clearing != null)
|
||||||
|
Clearing(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IEnumerable implementation
|
||||||
|
|
||||||
|
/// <summary>Returns a new object enumerator for the Dictionary</summary>
|
||||||
|
/// <returns>The new object enumerator</returns>
|
||||||
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
|
||||||
|
return (this.typedDictionary as IEnumerable).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDictionary implementation
|
||||||
|
|
||||||
|
/// <summary>Adds an item into the Dictionary</summary>
|
||||||
|
/// <param name="key">Key under which the item will be added</param>
|
||||||
|
/// <param name="value">Item that will be added</param>
|
||||||
|
void IDictionary.Add(object key, object value) {
|
||||||
|
this.objectDictionary.Add(key, value);
|
||||||
|
OnAdded(new KeyValuePair<KeyType, ValueType>((KeyType)key, (ValueType)value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified key exists in the Dictionary</summary>
|
||||||
|
/// <param name="key">Key that will be checked for</param>
|
||||||
|
/// <returns>True if an item with the specified key exists in the Dictionary</returns>
|
||||||
|
bool IDictionary.Contains(object key) {
|
||||||
|
return this.objectDictionary.Contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a new entry enumerator for the dictionary</summary>
|
||||||
|
/// <returns>The new entry enumerator</returns>
|
||||||
|
IDictionaryEnumerator IDictionary.GetEnumerator() {
|
||||||
|
return this.objectDictionary.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the size of the Dictionary is fixed</summary>
|
||||||
|
bool IDictionary.IsFixedSize {
|
||||||
|
get { return this.objectDictionary.IsFixedSize; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a collection of all keys in the Dictionary</summary>
|
||||||
|
ICollection IDictionary.Keys {
|
||||||
|
get { return this.objectDictionary.Keys; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a collection of all values stored in the Dictionary</summary>
|
||||||
|
ICollection IDictionary.Values {
|
||||||
|
get { return this.objectDictionary.Values; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes an item from the Dictionary</summary>
|
||||||
|
/// <param name="key">Key of the item that will be removed</param>
|
||||||
|
void IDictionary.Remove(object key) {
|
||||||
|
ValueType value;
|
||||||
|
bool removed = this.typedDictionary.TryGetValue((KeyType)key, out value);
|
||||||
|
this.objectDictionary.Remove(key);
|
||||||
|
if(removed) {
|
||||||
|
OnRemoved(new KeyValuePair<KeyType, ValueType>((KeyType)key, (ValueType)value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Accesses an item in the Dictionary by its key</summary>
|
||||||
|
/// <param name="key">Key of the item that will be accessed</param>
|
||||||
|
/// <returns>The item with the specified key</returns>
|
||||||
|
object IDictionary.this[object key] {
|
||||||
|
get { return this.objectDictionary[key]; }
|
||||||
|
set {
|
||||||
|
bool removed;
|
||||||
|
ValueType oldValue;
|
||||||
|
removed = this.typedDictionary.TryGetValue((KeyType)key, out oldValue);
|
||||||
|
|
||||||
|
this.objectDictionary[key] = value;
|
||||||
|
|
||||||
|
if(removed) {
|
||||||
|
OnRemoved(new KeyValuePair<KeyType, ValueType>((KeyType)key, oldValue));
|
||||||
|
}
|
||||||
|
OnAdded(new KeyValuePair<KeyType, ValueType>((KeyType)key, (ValueType)value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ICollection<> implementation
|
||||||
|
|
||||||
|
/// <summary>Inserts an already prepared element into the Dictionary</summary>
|
||||||
|
/// <param name="item">Prepared element that will be added to the Dictionary</param>
|
||||||
|
void ICollection<KeyValuePair<KeyType, ValueType>>.Add(
|
||||||
|
KeyValuePair<KeyType, ValueType> item
|
||||||
|
) {
|
||||||
|
this.typedDictionary.Add(item);
|
||||||
|
OnAdded(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the Dictionary</summary>
|
||||||
|
void ICollection<KeyValuePair<KeyType, ValueType>>.Clear() {
|
||||||
|
OnClearing();
|
||||||
|
this.typedDictionary.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the Dictionary</summary>
|
||||||
|
/// <param name="itemToRemove">Item that will be removed from the Dictionary</param>
|
||||||
|
bool ICollection<KeyValuePair<KeyType, ValueType>>.Remove(
|
||||||
|
KeyValuePair<KeyType, ValueType> itemToRemove
|
||||||
|
) {
|
||||||
|
bool removed = this.typedDictionary.Remove(itemToRemove);
|
||||||
|
if(removed) {
|
||||||
|
OnRemoved(itemToRemove);
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ICollection implementation
|
||||||
|
|
||||||
|
/// <summary>Copies the contents of the Dictionary into an array</summary>
|
||||||
|
/// <param name="array">Array the Dictionary contents will be copied into</param>
|
||||||
|
/// <param name="index">
|
||||||
|
/// Starting index at which to begin filling the destination array
|
||||||
|
/// </param>
|
||||||
|
void ICollection.CopyTo(Array array, int index) {
|
||||||
|
this.objectDictionary.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the Dictionary is synchronized for multi-threaded usage</summary>
|
||||||
|
bool ICollection.IsSynchronized {
|
||||||
|
get { return this.objectDictionary.IsSynchronized; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Synchronization root on which the Dictionary locks</summary>
|
||||||
|
object ICollection.SyncRoot {
|
||||||
|
get { return this.objectDictionary.SyncRoot; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#if !COMPACTFRAMEWORK
|
||||||
|
#region ISerializable implementation
|
||||||
|
|
||||||
|
/// <summary>Serializes the Dictionary</summary>
|
||||||
|
/// <param name="info">
|
||||||
|
/// Provides the container into which the Dictionary will serialize itself
|
||||||
|
/// </param>
|
||||||
|
/// <param name="context">
|
||||||
|
/// Contextual informations about the serialization environment
|
||||||
|
/// </param>
|
||||||
|
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
|
||||||
|
(this.typedDictionary as ISerializable).GetObjectData(info, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called after all objects have been successfully deserialized</summary>
|
||||||
|
/// <param name="sender">Nicht unterstützt</param>
|
||||||
|
void IDeserializationCallback.OnDeserialization(object sender) {
|
||||||
|
(this.typedDictionary as IDeserializationCallback).OnDeserialization(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#endif //!COMPACTFRAMEWORK
|
||||||
|
|
||||||
|
/// <summary>The wrapped Dictionary under its type-safe interface</summary>
|
||||||
|
private IDictionary<KeyType, ValueType> typedDictionary;
|
||||||
|
/// <summary>The wrapped Dictionary under its object interface</summary>
|
||||||
|
private IDictionary objectDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Collections
|
|
@ -188,7 +188,7 @@ namespace Nuclex.Support.Collections {
|
||||||
/// Add() method is called via the generic IDictionary<> interface
|
/// Add() method is called via the generic IDictionary<> interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test, ExpectedException(typeof(NotSupportedException))]
|
[Test, ExpectedException(typeof(NotSupportedException))]
|
||||||
public void TestThrowOnAddAtViaGenericIDictionary() {
|
public void TestThrowOnAddViaGenericIDictionary() {
|
||||||
Dictionary<int, string> numbers = createTestDictionary();
|
Dictionary<int, string> numbers = createTestDictionary();
|
||||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether the read only dictionary will throw an exception if its
|
/// Checks whether the read only dictionary will throw an exception if its
|
||||||
/// Clear() method is used via the generic ICollection<> interface
|
/// Remove() method is used via the generic ICollection<> interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test, ExpectedException(typeof(NotSupportedException))]
|
[Test, ExpectedException(typeof(NotSupportedException))]
|
||||||
public void TestThrowOnRemoveViaGenericICollection() {
|
public void TestThrowOnRemoveViaGenericICollection() {
|
||||||
|
@ -396,7 +396,7 @@ namespace Nuclex.Support.Collections {
|
||||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||||
|
|
||||||
(testDictionary as ICollection<KeyValuePair<int, string>>).Remove(
|
(testDictionary as ICollection<KeyValuePair<int, string>>).Remove(
|
||||||
new KeyValuePair<int, string>(24, "twenty-four")
|
new KeyValuePair<int, string>(42, "fourty-two")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,9 @@ using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace Nuclex.Support.Collections {
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
/// <summary>Wraps a Dictionary and prevents users from modifying it</summary>
|
/// <summary>Wraps a dictionary and prevents users from modifying it</summary>
|
||||||
/// <typeparam name="KeyType">Type of the keys used in the Dictionary</typeparam>
|
/// <typeparam name="KeyType">Type of the keys used in the dictionary</typeparam>
|
||||||
/// <typeparam name="ValueType">Type of the values used in the Dictionary</typeparam>
|
/// <typeparam name="ValueType">Type of the values used in the dictionary</typeparam>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ReadOnlyDictionary<KeyType, ValueType> :
|
public class ReadOnlyDictionary<KeyType, ValueType> :
|
||||||
#if !COMPACTFRAMEWORK
|
#if !COMPACTFRAMEWORK
|
||||||
|
@ -89,7 +89,7 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
#endif // !COMPACTFRAMEWORK
|
#endif // !COMPACTFRAMEWORK
|
||||||
|
|
||||||
/// <summary>Initializes a new read-only Dictionary wrapper</summary>
|
/// <summary>Initializes a new read-only dictionary wrapper</summary>
|
||||||
/// <param name="dictionary">Dictionary that will be wrapped</param>
|
/// <param name="dictionary">Dictionary that will be wrapped</param>
|
||||||
public ReadOnlyDictionary(IDictionary<KeyType, ValueType> dictionary) {
|
public ReadOnlyDictionary(IDictionary<KeyType, ValueType> dictionary) {
|
||||||
this.typedDictionary = dictionary;
|
this.typedDictionary = dictionary;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user