Fixed some typos in the comments; added weak collection which stores objects by weak reference; moved UNITTEST #if to the top of the files for some unit tests

git-svn-id: file:///srv/devel/repo-conversion/nusu@122 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2009-03-11 19:20:28 +00:00
parent 190e32ee56
commit 743fc9cc09
11 changed files with 1229 additions and 15 deletions

View File

@ -128,6 +128,13 @@
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Test.cs"> <Compile Include="Source\Collections\TransformingReadOnlyCollection.Test.cs">
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon> <DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Collections\WeakCollection.cs" />
<Compile Include="Source\Collections\WeakCollection.Interfaces.cs">
<DependentUpon>WeakCollection.cs</DependentUpon>
</Compile>
<Compile Include="Source\Collections\WeakCollection.Test.cs">
<DependentUpon>WeakCollection.cs</DependentUpon>
</Compile>
<Compile Include="Source\FloatHelper.cs" /> <Compile Include="Source\FloatHelper.cs" />
<Compile Include="Source\FloatHelper.Test.cs"> <Compile Include="Source\FloatHelper.Test.cs">
<DependentUpon>FloatHelper.cs</DependentUpon> <DependentUpon>FloatHelper.cs</DependentUpon>

View File

@ -110,6 +110,13 @@
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Test.cs"> <Compile Include="Source\Collections\TransformingReadOnlyCollection.Test.cs">
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon> <DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Collections\WeakCollection.cs" />
<Compile Include="Source\Collections\WeakCollection.Interfaces.cs">
<DependentUpon>WeakCollection.cs</DependentUpon>
</Compile>
<Compile Include="Source\Collections\WeakCollection.Test.cs">
<DependentUpon>WeakCollection.cs</DependentUpon>
</Compile>
<Compile Include="Source\FloatHelper.cs" /> <Compile Include="Source\FloatHelper.cs" />
<Compile Include="Source\FloatHelper.Test.cs"> <Compile Include="Source\FloatHelper.Test.cs">
<DependentUpon>FloatHelper.cs</DependentUpon> <DependentUpon>FloatHelper.cs</DependentUpon>

View File

@ -84,7 +84,7 @@ namespace Nuclex.Support.Collections {
} }
/// <summary> /// <summary>
/// Verifies that the CopyTo() of the transforming read only collection works /// Verifies that the CopyTo() method of the transforming read only collection works
/// </summary> /// </summary>
[Test] [Test]
public void TestCopyToArray() { public void TestCopyToArray() {
@ -99,7 +99,7 @@ namespace Nuclex.Support.Collections {
} }
/// <summary> /// <summary>
/// Verifies that the CopyTo() of the transforming read only collection throws /// Verifies that the CopyTo() method of the transforming read only collection throws
/// an exception if the target array is too small to hold the collection's contents /// an exception if the target array is too small to hold the collection's contents
/// </summary> /// </summary>
[Test, ExpectedException(typeof(ArgumentException))] [Test, ExpectedException(typeof(ArgumentException))]
@ -415,8 +415,8 @@ namespace Nuclex.Support.Collections {
} }
/// <summary> /// <summary>
/// Verifies that the CopyTo() of the transforming read only collection works /// Verifies that the CopyTo() method of the transforming read only collection
/// if invoked via the ICollection interface /// works if invoked via the ICollection interface
/// </summary> /// </summary>
[Test] [Test]
public void TestCopyToArrayViaICollection() { public void TestCopyToArrayViaICollection() {

View File

@ -0,0 +1,211 @@
#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.Threading;
using System.Collections;
using System.Collections.Generic;
namespace Nuclex.Support.Collections {
partial class WeakCollection<ItemType> {
#region IEnumerable Members
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>
/// A System.Collections.IEnumerator object that can be used to iterate through
/// the collection.
/// </returns>
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region IList Members
/// <summary>Adds an item to the WeakCollection.</summary>
/// <param name="value">The System.Object to add to the WeakCollection.</param>
/// <returns>The position into which the new element was inserted.</returns>
/// <exception cref="System.NotSupportedException">
/// The System.Collections.IList is read-only or the WeakCollection has a fixed size.
/// </exception>
int IList.Add(object value) {
ItemType valueAsItemType = downcastToItemType(value);
return (this.items as IList).Add(new WeakReference<ItemType>(valueAsItemType));
}
/// <summary>
/// Determines whether the WeakCollection contains a specific value.
/// </summary>
/// <param name="value">The System.Object to locate in the WeakCollection.</param>
/// <returns>
/// True if the System.Object is found in the WeakCollection; otherwise, false.
/// </returns>
bool IList.Contains(object value) {
ItemType valueAsItemType = downcastToItemType(value);
return Contains(valueAsItemType);
}
/// <summary>Determines the index of a specific item in the WeakCollection.</summary>
/// <param name="value">The System.Object to locate in the WeakCollection.</param>
/// <returns>
/// The index of value if found in the list; otherwise, -1.
/// </returns>
int IList.IndexOf(object value) {
ItemType valueAsItemType = downcastToItemType(value);
return IndexOf(valueAsItemType);
}
/// <summary>
/// Inserts an item to the WeakCollection at the specified index.
/// </summary>
/// <param name="index">
/// The zero-based index at which value should be inserted.
/// </param>
/// <param name="value">The System.Object to insert into the WeakCollection.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Index is not a valid index in the TransformingReadOnlyCollection.
/// </exception>
/// <exception cref="System.NotSupportedException">
/// The System.Collections.IList is read-only or the WeakCollection has a fixed size.
/// </exception>
/// <exception cref="System.NullReferenceException">
/// Value is null reference in the WeakCollection.
/// </exception>
void IList.Insert(int index, object value) {
ItemType valueAsItemType = downcastToItemType(value);
Insert(index, valueAsItemType);
}
/// <summary>
/// A value indicating whether the WeakCollection has a fixed size.
/// </summary>
bool IList.IsFixedSize {
get { return (this.items as IList).IsFixedSize; }
}
/// <summary>
/// Removes the first occurrence of a specific object from the WeakCollection.
/// </summary>
/// <param name="value">The System.Object to remove from the WeakCollection.</param>
/// <exception cref="System.NotSupportedException">
/// The WeakCollection is read-only or the WeakCollection has a fixed size.
/// </exception>
void IList.Remove(object value) {
ItemType valueAsItemType = downcastToItemType(value);
Remove(valueAsItemType);
}
/// <summary>Gets or sets the element at the specified index.</summary>
/// <param name="index">The zero-based index of the element to get or set.</param>
/// <returns>The element at the specified index</returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Index is not a valid index in the WeakCollection
/// </exception>
object IList.this[int index] {
get { return this[index]; }
set {
ItemType valueAsItemType = downcastToItemType(value);
this[index] = valueAsItemType;
}
}
#endregion
#region ICollection Members
/// <summary>
/// Copies the elements of the WeakCollection to an System.Array, starting at
/// a particular System.Array index.
/// </summary>
/// <param name="array">
/// The one-dimensional System.Array that is the destination of the elements
/// copied from WeakCollection. The System.Array must have zero-based indexing.
/// </param>
/// <param name="index">The zero-based index in array at which copying begins.</param>
/// <exception cref="System.ArgumentNullException">
/// Array is null.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Index is less than zero.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Array is multidimensional or index is equal to or greater than the length
/// of array or the number of elements in the source WeakCollection is greater than
/// the available space from index to the end of the destination array.
/// </exception>
/// <exception cref="System.InvalidCastException">
/// The type of the source WeakCollection cannot be cast automatically to the type of
/// the destination array.
/// </exception>
void ICollection.CopyTo(Array array, int index) {
CopyTo((ItemType[])array, index);
}
/// <summary>
/// A value indicating whether access to the WeakCollection is
/// synchronized (thread safe).
/// </summary>
bool ICollection.IsSynchronized {
get { return false; }
}
/// <summary>
/// An object that can be used to synchronize access to the WeakCollection.
/// </summary>
object ICollection.SyncRoot {
get {
if(this.syncRoot == null) {
ICollection is2 = this.items as ICollection;
if(is2 != null) {
this.syncRoot = is2.SyncRoot;
} else {
Interlocked.CompareExchange(ref this.syncRoot, new object(), null);
}
}
return this.syncRoot;
}
}
#endregion
/// <summary>
/// Downcasts an object reference to a reference to the collection's item type
/// </summary>
/// <param name="value">Object reference that will be downcast</param>
/// <returns>
/// The specified object referecne as a reference to the collection's item type
/// </returns>
private static ItemType downcastToItemType(object value) {
ItemType valueAsItemType = value as ItemType;
if(!ReferenceEquals(value, null)) {
if(valueAsItemType == null) {
throw new ArgumentException("Object is not of a compatible type", "value");
}
}
return valueAsItemType;
}
}
} // namespace Nuclex.Support.Collections

View File

@ -0,0 +1,655 @@
#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;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
using NMock2;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the weak collection wrapper</summary>
[TestFixture]
public class WeakCollectionTest {
#region class Dummy
/// <summary>Dummy class used to test the weakly referencing collection</summary>
private class Dummy {
/// <summary>Initializes a new dummy</summary>
/// <param name="value">Value that will be stored by the dummy</param>
public Dummy(int value) {
this.Value = value;
}
/// <summary>
/// Determines whether the specified System.Object is equal to
/// the current Dummy object.
/// </summary>
/// <param name="otherAsObject">
/// The System.Object to compare with the current Dummy object
/// </param>
/// <returns>
/// True if the specified System.Object is equal to the current Dummy object;
/// otherwise, false.
/// </returns>
public override bool Equals(object otherAsObject) {
Dummy other = otherAsObject as Dummy;
if(other == null) {
return false;
}
return this.Value.Equals(other.Value);
}
/// <summary>Serves as a hash function for a particular type.</summary>
/// <returns>A hash code for the current System.Object.</returns>
public override int GetHashCode() {
return this.Value.GetHashCode();
}
/// <summary>Some value that can be used for testing</summary>
public int Value;
}
#endregion // class Dummy
/// <summary>Verifies that the constructor of the weak collection is working</summary>
[Test]
public void TestConstructor() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.IsNotNull(dummies);
}
/// <summary>
/// Test whether the non-typesafe Add() method of the weak collection works
/// </summary>
[Test]
public void TestAddAsObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
(dummies as IList).Add((object)oneTwoThreeDummy);
CollectionAssert.Contains(dummies, oneTwoThreeDummy);
}
/// <summary>
/// Test whether the non-typesafe Add() method throws an exception if an object is
/// added that is not compatible to the collection's item type
/// </summary>
[Test, ExpectedException(typeof(ArgumentException))]
public void TestThrowOnAddIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
(dummies as IList).Add(new object());
}
/// <summary>
/// Test whether the generic Add() method of the weak collection works
/// </summary>
[Test]
public void TestAdd() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
CollectionAssert.Contains(dummies, oneTwoThreeDummy);
}
/// <summary>Tests whether the Clear() method works</summary>
[Test]
public void TestClear() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
Dummy threeTwoOneDummy = new Dummy(54321);
dummies.Add(threeTwoOneDummy);
Assert.AreEqual(2, dummies.Count);
dummies.Clear();
Assert.AreEqual(0, dummies.Count);
}
/// <summary>Tests whether the Contains() method works</summary>
[Test]
public void TestContains() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
Dummy threeTwoOneDummy = new Dummy(54321);
Assert.IsTrue(dummies.Contains(oneTwoThreeDummy));
Assert.IsFalse(dummies.Contains(threeTwoOneDummy));
}
/// <summary>Tests whether the non-typesafe Contains() method works</summary>
[Test]
public void TestContainsWithObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
Dummy threeTwoOneDummy = new Dummy(54321);
Assert.IsTrue((dummies as IList).Contains((object)oneTwoThreeDummy));
Assert.IsFalse((dummies as IList).Contains((object)threeTwoOneDummy));
}
/// <summary>
/// Verifies that the Enumerator of the dummy collection correctly
/// implements the Reset() method
/// </summary>
[Test]
public void TestEnumeratorReset() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
IEnumerator<Dummy> dummyEnumerator = dummies.GetEnumerator();
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsFalse(dummyEnumerator.MoveNext());
dummyEnumerator.Reset();
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsFalse(dummyEnumerator.MoveNext());
}
/// <summary>Verifies that the IndexOf() method is working as intended</summary>
[Test]
public void TestIndexOf() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy sevenEightNineDummy = new Dummy(789);
Assert.AreEqual(0, dummies.IndexOf(oneTwoThreeDummy));
Assert.AreEqual(1, dummies.IndexOf(fourFiveSixDummy));
Assert.AreEqual(-1, dummies.IndexOf(sevenEightNineDummy));
}
/// <summary>
/// Verifies that the non-typesafe IndexOf() method is working as intended
/// </summary>
[Test]
public void TestIndexOfWithObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy sevenEightNineDummy = new Dummy(789);
Assert.AreEqual(0, (dummies as IList).IndexOf((object)oneTwoThreeDummy));
Assert.AreEqual(1, (dummies as IList).IndexOf((object)fourFiveSixDummy));
Assert.AreEqual(-1, (dummies as IList).IndexOf((object)sevenEightNineDummy));
}
/// <summary>
/// Verifies that an exception is thrown if an incompatible object is passed to
/// the non-typesafe variant of the IndexOf() method
/// </summary>
[Test, ExpectedException(typeof(ArgumentException))]
public void TestThrowOnIndexOfWithIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.IsNull((dummies as IList).IndexOf(new object()));
}
/// <summary>Test whether the IndexOf() method can cope with null references</summary>
[Test]
public void TestIndexOfNull() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.AreEqual(-1, dummies.IndexOf(null));
dummies.Add(null);
Assert.AreEqual(0, dummies.IndexOf(null));
}
/// <summary>
/// Verifies that the CopyTo() method of the weak collection works
/// </summary>
[Test]
public void TestCopyToArray() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy[] inputDummies = new Dummy[] { oneTwoThreeDummy, fourFiveSixDummy };
Dummy[] outputDummies = new Dummy[dummies.Count];
dummies.CopyTo(outputDummies, 0);
CollectionAssert.AreEqual(inputDummies, outputDummies);
}
/// <summary>
/// Verifies that the CopyTo() method of the weak collection throws an exception
/// if the target array is too small to hold the collection's contents
/// </summary>
[Test, ExpectedException(typeof(ArgumentException))]
public void TestThrowOnCopyToTooSmallArray() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy[] outputStrings = new Dummy[dummies.Count - 1];
dummies.CopyTo(outputStrings, 0);
}
/// <summary>
/// Verifies that the CopyTo() method of the transforming read only collection
/// works if invoked via the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy[] inputDummies = new Dummy[] { oneTwoThreeDummy, fourFiveSixDummy };
Dummy[] outputDummies = new Dummy[dummies.Count];
(dummies as ICollection).CopyTo(outputDummies, 0);
CollectionAssert.AreEqual(inputDummies, outputDummies);
}
/// <summary>
/// Verifies that the Insert() method correctly shifts items in the collection
/// </summary>
[Test]
public void TestInsert() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Insert(0, fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
Assert.AreSame(fourFiveSixDummy, dummies[0]);
Assert.AreSame(oneTwoThreeDummy, dummies[1]);
}
/// <summary>
/// Verifies that the non-typesafe Insert() method correctly shifts items in
/// the collection
/// </summary>
[Test]
public void TestInsertObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
(dummies as IList).Insert(0, (object)fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
Assert.AreSame(fourFiveSixDummy, dummies[0]);
Assert.AreSame(oneTwoThreeDummy, dummies[1]);
}
/// <summary>
/// Verifies that the non-typesafe Insert() method correctly shifts items in
/// the collection
/// </summary>
[Test, ExpectedException(typeof(ArgumentException))]
public void TestThrowOnInsertIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
(dummies as IList).Insert(0, new object());
}
/// <summary>
/// Checks whether the IsFixedSize property of the weak collection returns
/// the expected result for a weak collection based on a fixed array
/// </summary>
[Test]
public void TestIsFixedSizeViaIList() {
Dummy oneTwoThreeDummy = new Dummy(123);
Dummy fourFiveSixDummy = new Dummy(456);
WeakReference<Dummy>[] dummyReferences = new WeakReference<Dummy>[] {
new WeakReference<Dummy>(oneTwoThreeDummy),
new WeakReference<Dummy>(fourFiveSixDummy)
};
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
Assert.IsTrue((dummies as IList).IsFixedSize);
}
/// <summary>
/// Tests whether the IsReadOnly property of the weak collection works
/// </summary>
[Test]
public void TestIsReadOnly() {
Dummy oneTwoThreeDummy = new Dummy(123);
Dummy fourFiveSixDummy = new Dummy(456);
List<WeakReference<Dummy>> dummyReferences = new List<WeakReference<Dummy>>();
dummyReferences.Add(new WeakReference<Dummy>(oneTwoThreeDummy));
dummyReferences.Add(new WeakReference<Dummy>(fourFiveSixDummy));
ReadOnlyList<WeakReference<Dummy>> readOnlyDummyReferences =
new ReadOnlyList<WeakReference<Dummy>>(dummyReferences);
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
WeakCollection<Dummy> readOnlydummies = new WeakCollection<Dummy>(
readOnlyDummyReferences
);
Assert.IsFalse(dummies.IsReadOnly);
Assert.IsTrue(readOnlydummies.IsReadOnly);
}
/// <summary>
/// Tests whether the IsSynchronized property of the weak collection works
/// </summary>
[Test]
public void TestIsSynchronized() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.IsFalse((dummies as IList).IsSynchronized);
}
/// <summary>Tests the indexer of the weak collection</summary>
[Test]
public void TestIndexer() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
Assert.AreSame(fourFiveSixDummy, dummies[1]);
dummies[0] = fourFiveSixDummy;
Assert.AreSame(fourFiveSixDummy, dummies[0]);
}
/// <summary>Tests the non-typesafe indexer of the weak collection</summary>
[Test]
public void TestIndexerWithObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreSame((object)oneTwoThreeDummy, (dummies as IList)[0]);
Assert.AreSame((object)fourFiveSixDummy, (dummies as IList)[1]);
(dummies as IList)[0] = (object)fourFiveSixDummy;
Assert.AreSame((object)fourFiveSixDummy, (dummies as IList)[0]);
}
/// <summary>
/// Tests whether the non-typesafe indexer of the weak collection throws
/// the correct exception if an incompatible object is assigned
/// </summary>
[Test, ExpectedException(typeof(ArgumentException))]
public void TestThrowOnIndexerWithIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
(dummies as IList)[0] = new object();
}
/// <summary>Tests the Remove() method of the weak collection</summary>
[Test]
public void TestRemove() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
Assert.IsTrue(dummies.Remove(oneTwoThreeDummy));
Assert.AreEqual(1, dummies.Count);
Assert.IsFalse(dummies.Remove(oneTwoThreeDummy));
}
/// <summary>Tests the non-typesafe Remove() method of the weak collection</summary>
[Test]
public void TestRemoveObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
(dummies as IList).Remove((object)oneTwoThreeDummy);
Assert.AreEqual(1, dummies.Count);
}
/// <summary>
/// Tests whether a null object can be managed by and removed from the weak collection
/// </summary>
[Test]
public void TestRemoveNull() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
dummies.Add(null);
Assert.AreEqual(1, dummies.Count);
Assert.IsTrue(dummies.Remove(null));
Assert.AreEqual(0, dummies.Count);
}
/// <summary>
/// Tests whether the non-typesafe Remove() method of the weak collection throws
/// an exception if an object is tried to be removed that is incompatible with
/// the collection's item type
/// </summary>
[Test, ExpectedException(typeof(ArgumentException))]
public void TestThrowOnRemoveIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
(dummies as IList).Remove(new object());
}
/// <summary>Tests the RemoveAt() method of the weak collection</summary>
[Test]
public void TestRemoveAt() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
dummies.RemoveAt(0);
Assert.AreSame(fourFiveSixDummy, dummies[0]);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
if(!(dummies as ICollection).IsSynchronized) {
lock((dummies as ICollection).SyncRoot) {
Assert.AreEqual(0, dummies.Count);
}
}
}
private class ListWithoutICollection : IList<WeakReference<Dummy>> {
public int IndexOf(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public void Insert(int index, WeakReference<Dummy> item) {
throw new NotImplementedException();
}
public void RemoveAt(int index) { throw new NotImplementedException(); }
public WeakReference<Dummy> this[int index] {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public void Add(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public void Clear() { throw new NotImplementedException(); }
public bool Contains(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public void CopyTo(WeakReference<Dummy>[] array, int arrayIndex) {
throw new NotImplementedException();
}
public int Count { get { return 12345; } }
public bool IsReadOnly { get { throw new NotImplementedException(); } }
public bool Remove(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public IEnumerator<WeakReference<Dummy>> GetEnumerator() {
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// on transforming read only collections based on IList&lt;&gt;s that do not
/// implement the ICollection interface
/// </summary>
[Test]
public void TestSynchronizationOfIListWithoutICollection() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new ListWithoutICollection()
);
if(!(dummies as ICollection).IsSynchronized) {
lock((dummies as ICollection).SyncRoot) {
int count = dummies.Count;
Assert.AreEqual(12345, count); // ;-)
}
}
}
/// <summary>Tests the RemoveDeadItems() method</summary>
[Test]
public void TestRemoveDeadItems() {
List<WeakReference<Dummy>> dummyReferences = new List<WeakReference<Dummy>>();
Dummy oneTwoThreeDummy = new Dummy(123);
dummyReferences.Add(new WeakReference<Dummy>(oneTwoThreeDummy));
dummyReferences.Add(new WeakReference<Dummy>(null));
Dummy fourFiveSixDummy = new Dummy(456);
dummyReferences.Add(new WeakReference<Dummy>(fourFiveSixDummy));
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
Assert.AreEqual(3, dummies.Count);
dummies.RemoveDeadItems();
Assert.AreEqual(2, dummies.Count);
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
Assert.AreSame(fourFiveSixDummy, dummies[1]);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View File

@ -0,0 +1,335 @@
#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;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Nuclex.Support.Collections {
/// <summary>Collection of weakly referenced objects</summary>
/// <remarks>
/// This collection tries to expose the interface of a normal collection, but stores
/// objects as weak references. When an object is accessed, it can return null.
/// when the collection detects that one of its items was garbage collected, it
/// will silently remove that item.
/// </remarks>
public partial class WeakCollection<ItemType> : IList<ItemType>, IList
where ItemType : class {
#region class UnpackingEnumerator
/// <summary>
/// An enumerator that unpacks the items returned by an enumerator of the
/// weak reference collection into the actual item type on-the-fly.
/// </summary>
private class UnpackingEnumerator : IEnumerator<ItemType> {
/// <summary>Initializes a new unpacking enumerator</summary>
/// <param name="containedTypeEnumerator">
/// Enumerator of the weak reference collection
/// </param>
public UnpackingEnumerator(
IEnumerator<WeakReference<ItemType>> containedTypeEnumerator
) {
this.containedTypeEnumerator = containedTypeEnumerator;
}
/// <summary>Immediately releases all resources used by the instance</summary>
public void Dispose() {
this.containedTypeEnumerator.Dispose();
}
/// <summary>
/// The element in the collection at the current position of the enumerator.
/// </summary>
public ItemType Current {
get { return this.containedTypeEnumerator.Current.Target; }
}
/// <summary>Gets the current element in the collection.</summary>
/// <returns>The current element in the collection.</returns>
/// <exception cref="System.InvalidOperationException">
/// The enumerator is positioned before the first element of the collection
/// or after the last element.
/// </exception>
public bool MoveNext() {
return this.containedTypeEnumerator.MoveNext();
}
/// <summary>
/// Sets the enumerator to its initial position, which is before the first element
/// in the collection.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// The collection was modified after the enumerator was created.
/// </exception>
public void Reset() {
this.containedTypeEnumerator.Reset();
}
/// <summary>The current element in the collection.</summary>
/// <exception cref="System.InvalidOperationException">
/// The enumerator is positioned before the first element of the collection
/// or after the last element.
/// </exception>
object IEnumerator.Current {
get { return Current; }
}
/// <summary>An enumerator from the wrapped collection</summary>
private IEnumerator<WeakReference<ItemType>> containedTypeEnumerator;
}
#endregion // class UnpackingEnumerator
/// <summary>Initializes a new weak reference collection</summary>
/// <param name="items">
/// Internal list of weak references that are unpacking when accessed through
/// the WeakCollection's interface.
/// </param>
public WeakCollection(IList<WeakReference<ItemType>> items) {
this.items = items;
}
/// <summary>
/// Determines whether an element is in the WeakCollection
/// </summary>
/// <param name="item">
/// The object to locate in the WeakCollection. The value can be null.
/// </param>
/// <returns>
/// True if value is found in the WeakCollection; otherwise, false.
/// </returns>
/// <remarks>
/// The default implementation of this method is very unoptimized and will
/// enumerate all the items in the collection, transforming one after another
/// to check whether the transformed item matches the item the user was
/// looking for. It is recommended to provide a custom implementation of
/// this method, if possible.
/// </remarks>
public virtual bool Contains(ItemType item) {
return (IndexOf(item) != -1);
}
/// <summary>
/// Copies the entire WeakCollection to a compatible one-dimensional
/// System.Array, starting at the specified index of the target array.
/// </summary>
/// <param name="array">
/// The one-dimensional System.Array that is the destination of the elements copied
/// from the WeakCollection. The System.Array must have zero-based indexing.
/// </param>
/// <param name="index">
/// The zero-based index in array at which copying begins.
/// </param>
/// <exception cref="System.ArgumentException">
/// Index is equal to or greater than the length of array or the number of elements
/// in the source WeakCollection is greater than the available space from index to
/// the end of the destination array.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Index is less than zero.
/// </exception>
/// <exception cref="System.ArgumentNullException">
/// Array is null.
/// </exception>
public void CopyTo(ItemType[] array, int index) {
if(this.items.Count > (array.Length - index)) {
throw new ArgumentException(
"Array too small to fit the collection items starting at the specified index"
);
}
for(int itemIndex = 0; itemIndex < this.items.Count; ++itemIndex) {
array[itemIndex + index] = this.items[itemIndex].Target;
}
}
/// <summary>Removes all items from the WeakCollection</summary>
public void Clear() {
this.items.Clear();
}
/// <summary>
/// Returns an enumerator that iterates through the WeakCollection.
/// </summary>
/// <returns>An enumerator or the WeakCollection.</returns>
public IEnumerator<ItemType> GetEnumerator() {
return new UnpackingEnumerator(this.items.GetEnumerator());
}
/// <summary>
/// Searches for the specified object and returns the zero-based index of the
/// first occurrence within the entire WeakCollection.
/// </summary>
/// <param name="item">
/// The object to locate in the WeakCollection. The value can
/// be null for reference types.
/// </param>
/// <returns>
/// The zero-based index of the first occurrence of item within the entire
/// WeakCollection, if found; otherwise, -1.
/// </returns>
/// <remarks>
/// The default implementation of this method is very unoptimized and will
/// enumerate all the items in the collection, transforming one after another
/// to check whether the transformed item matches the item the user was
/// looking for. It is recommended to provide a custom implementation of
/// this method, if possible.
/// </remarks>
public int IndexOf(ItemType item) {
EqualityComparer<ItemType> comparer = EqualityComparer<ItemType>.Default;
for(int index = 0; index < this.items.Count; ++index) {
ItemType itemAtIndex = this.items[index].Target;
if((itemAtIndex == null) || (item == null)) {
if(ReferenceEquals(item, itemAtIndex)) {
return index;
}
} else {
if(comparer.Equals(itemAtIndex, item)) {
return index;
}
}
}
return -1;
}
/// <summary>
/// The number of elements contained in the WeakCollection instance
/// </summary>
public int Count {
get { return this.items.Count; }
}
/// <summary>Gets the element at the specified index.</summary>
/// <param name="index">The zero-based index of the element to get.</param>
/// <returns>The element at the specified index.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Index is less than zero or index is equal to or greater than
/// WeakCollection.Count.
/// </exception>
public ItemType this[int index] {
get { return this.items[index].Target; }
set { this.items[index] = new WeakReference<ItemType>(value); }
}
/// <summary>
/// Removes the first occurrence of a specific object from the WeakCollection.
/// </summary>
/// <param name="item">The object to remove from the WeakCollection</param>
/// <returns>
/// True if item was successfully removed from the WeakCollection; otherwise, false.
/// </returns>
public bool Remove(ItemType item) {
EqualityComparer<ItemType> comparer = EqualityComparer<ItemType>.Default;
lock(this) {
for(int index = 0; index < this.items.Count; ++index) {
ItemType itemAtIndex = this.items[index].Target;
if((itemAtIndex == null) || (item == null)) {
if(ReferenceEquals(item, itemAtIndex)) {
this.items.RemoveAt(index);
return true;
}
} else {
if(comparer.Equals(item, itemAtIndex)) {
this.items.RemoveAt(index);
return true;
}
}
}
} // lock(this)
return false;
}
/// <summary>Adds an item to the WeakCollection.</summary>
/// <param name="item">The object to add to the WeakCollection</param>
public void Add(ItemType item) {
this.items.Add(new WeakReference<ItemType>(item));
}
/// <summary>Inserts an item to the WeakCollection at the specified index.</summary>
/// <param name="index">
/// The zero-based index at which item should be inserted.
/// </param>
/// <param name="item">The object to insert into the WeakCollection</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// index is not a valid index in the WeakCollection.
/// </exception>
public void Insert(int index, ItemType item) {
this.items.Insert(index, new WeakReference<ItemType>(item));
}
/// <summary>
/// Removes the WeakCollection item at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the item to remove.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Index is not a valid index in the WeakCollection.
/// </exception>
public void RemoveAt(int index) {
this.items.RemoveAt(index);
}
/// <summary>Whether the List is write-protected</summary>
public bool IsReadOnly {
get { return this.items.IsReadOnly; }
}
/// <summary>
/// Removes the items that have been garbage collected from the collection
/// </summary>
public void RemoveDeadItems() {
lock(this) {
int eliminatedItemCount = 0;
// Eliminate all items that have been garbage collected by shifting
for(int index = 0; index + eliminatedItemCount < this.items.Count; ++index) {
if(!this.items[index].IsAlive) {
++eliminatedItemCount;
this.items[index] = this.items[index + eliminatedItemCount];
}
}
// If any garbage collected items were found, resize the collection so
// the space that became empty in the previous shifting process will be freed
if(eliminatedItemCount > 0) {
int endIndex = this.items.Count - eliminatedItemCount;
for(int index = this.items.Count - 1; index >= endIndex; --index) {
this.items.RemoveAt(index);
}
}
}
}
/// <summary>Weak references to the items contained in the collection</summary>
private IList<WeakReference<ItemType>> items;
/// <summary>Synchronization root for threaded accesses to this collection</summary>
private object syncRoot;
}
} // namespace Nuclex.Support.Collections

View File

@ -18,11 +18,11 @@ License along with this library
*/ */
#endregion #endregion
#if UNITTEST
using System; using System;
using System.IO; using System.IO;
#if UNITTEST
using NUnit.Framework; using NUnit.Framework;
namespace Nuclex.Support.Tracking { namespace Nuclex.Support.Tracking {

View File

@ -18,11 +18,11 @@ License along with this library
*/ */
#endregion #endregion
#if UNITTEST
using System; using System;
using System.IO; using System.IO;
#if UNITTEST
using NUnit.Framework; using NUnit.Framework;
namespace Nuclex.Support.Tracking { namespace Nuclex.Support.Tracking {

View File

@ -18,14 +18,14 @@ License along with this library
*/ */
#endregion #endregion
#if UNITTEST
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Nuclex.Support.Scheduling; using Nuclex.Support.Scheduling;
#if UNITTEST
using NUnit.Framework; using NUnit.Framework;
using NMock2; using NMock2;
@ -124,7 +124,6 @@ namespace Nuclex.Support.Tracking {
failedDummy.Join(); failedDummy.Join();
} }
} }
} // namespace Nuclex.Support.Tracking } // namespace Nuclex.Support.Tracking

View File

@ -18,11 +18,11 @@ License along with this library
*/ */
#endregion #endregion
#if UNITTEST
using System; using System;
using System.IO; using System.IO;
#if UNITTEST
using NUnit.Framework; using NUnit.Framework;
namespace Nuclex.Support.Tracking { namespace Nuclex.Support.Tracking {

View File

@ -18,11 +18,11 @@ License along with this library
*/ */
#endregion #endregion
#if UNITTEST
using System; using System;
using System.IO; using System.IO;
#if UNITTEST
using NUnit.Framework; using NUnit.Framework;
namespace Nuclex.Support.Tracking { namespace Nuclex.Support.Tracking {