Added read-only collection wrappers for IDictionary<>, IList<> and ICollection<> since the classes provided by the .NET framework in System.Collections.ObjectModel are incomplete and inconsistent (eg. System.Collections.ObjectModel.ReadOnlyCollection wraps an IList<>!); minor improvements to documentation; added ReverseComparer for inverting the results of an existing IComparer<> or simply comparing values in inverted order
git-svn-id: file:///srv/devel/repo-conversion/nusu@80 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
f5eefc6765
commit
2cc7b27b0c
|
@ -81,6 +81,8 @@
|
||||||
<Reference Include="System">
|
<Reference Include="System">
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@ -100,6 +102,10 @@
|
||||||
<Compile Include="Source\Collections\PriorityQueue.Test.cs">
|
<Compile Include="Source\Collections\PriorityQueue.Test.cs">
|
||||||
<DependentUpon>PriorityQueue.cs</DependentUpon>
|
<DependentUpon>PriorityQueue.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\Collections\ReadOnlyCollection.cs" />
|
||||||
|
<Compile Include="Source\Collections\ReadOnlyDictionary.cs" />
|
||||||
|
<Compile Include="Source\Collections\ReadOnlyList.cs" />
|
||||||
|
<Compile Include="Source\Collections\ReverseComparer.cs" />
|
||||||
<Compile Include="Source\Collections\RingMemoryStream.cs" />
|
<Compile Include="Source\Collections\RingMemoryStream.cs" />
|
||||||
<Compile Include="Source\Collections\RingMemoryStream.Test.cs">
|
<Compile Include="Source\Collections\RingMemoryStream.Test.cs">
|
||||||
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
||||||
|
|
|
@ -127,7 +127,7 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
ItemType result = this.heap[0];
|
ItemType result = this.heap[0];
|
||||||
--this.count;
|
--this.count;
|
||||||
trickleDown(0, heap[this.count]);
|
trickleDown(0, this.heap[this.count]);
|
||||||
|
|
||||||
++this.version;
|
++this.version;
|
||||||
|
|
||||||
|
|
120
Source/Collections/ReadOnlyCollection.cs
Normal file
120
Source/Collections/ReadOnlyCollection.cs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
/// <summary>Wraps a Collection and prevents users from modifying it</summary>
|
||||||
|
/// <typeparam name="ItemType">Type of items to manage in the Collection</typeparam>
|
||||||
|
public class ReadOnlyCollection<ItemType> :
|
||||||
|
ICollection<ItemType>,
|
||||||
|
ICollection {
|
||||||
|
|
||||||
|
/// <summary>Initializes a new read-only Collection wrapper</summary>
|
||||||
|
/// <param name="collection">Collection that will be wrapped</param>
|
||||||
|
public ReadOnlyCollection(ICollection<ItemType> collection) {
|
||||||
|
this.typedCollection = collection;
|
||||||
|
this.objectCollection = (collection as ICollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the List contains the specified item</summary>
|
||||||
|
/// <param name="item">Item that will be checked for</param>
|
||||||
|
/// <returns>True if the specified item is contained in the List</returns>
|
||||||
|
public bool Contains(ItemType item) {
|
||||||
|
return this.typedCollection.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Copies the contents of the List into an array</summary>
|
||||||
|
/// <param name="array">Array the List will be copied into</param>
|
||||||
|
/// <param name="arrayIndex">
|
||||||
|
/// Starting index at which to begin filling the destination array
|
||||||
|
/// </param>
|
||||||
|
public void CopyTo(ItemType[] array, int arrayIndex) {
|
||||||
|
this.typedCollection.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The number of items current contained in the List</summary>
|
||||||
|
public int Count {
|
||||||
|
get { return this.typedCollection.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the List is write-protected</summary>
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a new enumerator over the contents of the List</summary>
|
||||||
|
/// <returns>The new List contents enumerator</returns>
|
||||||
|
public IEnumerator<ItemType> GetEnumerator() {
|
||||||
|
return this.typedCollection.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region ICollection<> implementation
|
||||||
|
|
||||||
|
/// <summary>Adds an item to the end of the List</summary>
|
||||||
|
/// <param name="item">Item that will be added to the List</param>
|
||||||
|
void ICollection<ItemType>.Add(ItemType item) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Adding items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the List</summary>
|
||||||
|
void ICollection<ItemType>.Clear() {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Clearing is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes the specified item from the List</summary>
|
||||||
|
/// <param name="item">Item that will be removed from the List</param>
|
||||||
|
/// <returns>True of the specified item was found in the List and removed</returns>
|
||||||
|
bool ICollection<ItemType>.Remove(ItemType item) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IEnumerable implementation
|
||||||
|
|
||||||
|
/// <summary>Returns a new enumerator over the contents of the List</summary>
|
||||||
|
/// <returns>The new List contents enumerator</returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return this.objectCollection.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ICollection implementation
|
||||||
|
|
||||||
|
/// <summary>Copies the contents of the List into an array</summary>
|
||||||
|
/// <param name="array">Array the List 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) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the List is synchronized for multi-threaded usage</summary>
|
||||||
|
bool ICollection.IsSynchronized {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Synchronization root on which the List locks</summary>
|
||||||
|
object ICollection.SyncRoot {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>The wrapped Collection under its type-safe interface</summary>
|
||||||
|
private ICollection<ItemType> typedCollection;
|
||||||
|
/// <summary>The wrapped Collection under its object interface</summary>
|
||||||
|
private ICollection objectCollection;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Collections
|
326
Source/Collections/ReadOnlyDictionary.cs
Normal file
326
Source/Collections/ReadOnlyDictionary.cs
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
/// <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="ValueType">Type of the values used in the Dictionary</typeparam>
|
||||||
|
public class ReadOnlyDictionary<KeyType, ValueType> :
|
||||||
|
IDictionary<KeyType, ValueType>,
|
||||||
|
IDictionary,
|
||||||
|
ISerializable,
|
||||||
|
IDeserializationCallback {
|
||||||
|
|
||||||
|
/// <summary>Initializes a new read-only Dictionary wrapper</summary>
|
||||||
|
/// <param name="dictionary">Dictionary that will be wrapped</param>
|
||||||
|
public ReadOnlyDictionary(IDictionary<KeyType, ValueType> dictionary) {
|
||||||
|
this.typedDictionary = dictionary;
|
||||||
|
this.objectDictionary = (this.typedDictionary as IDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the directory is write-protected</summary>
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 {
|
||||||
|
if(this.readonlyKeyCollection == null) {
|
||||||
|
this.readonlyKeyCollection = new ReadOnlyCollection<KeyType>(
|
||||||
|
this.typedDictionary.Keys
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.readonlyKeyCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Collection aller Werte im Dictionary</summary>
|
||||||
|
public ICollection<ValueType> Values { // TODO: RO-Wrappen!
|
||||||
|
get {
|
||||||
|
if(this.readonlyValueCollection == null) {
|
||||||
|
this.readonlyValueCollection = new ReadOnlyCollection<ValueType>(
|
||||||
|
this.typedDictionary.Values
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.readonlyValueCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDictionary<,> implementation
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
void IDictionary<KeyType, ValueType>.Add(KeyType key, ValueType value) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Adding items is not supported by the read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
bool IDictionary<KeyType, ValueType>.Remove(KeyType key) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported by the read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Accesses an item in the Dictionary by its key</summary>
|
||||||
|
/// <param name="key">Key of the item that will be accessed</param>
|
||||||
|
ValueType IDictionary<KeyType, ValueType>.this[KeyType key] {
|
||||||
|
get { return this.typedDictionary[key]; }
|
||||||
|
set {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Assigning items is not supported in a read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#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>Removes all items from the Dictionary</summary>
|
||||||
|
void IDictionary.Clear() {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Clearing is not supported in a read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Adding items is not supported in a read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 {
|
||||||
|
if(this.readonlyKeyCollection == null) {
|
||||||
|
this.readonlyKeyCollection = new ReadOnlyCollection<KeyType>(
|
||||||
|
this.typedDictionary.Keys
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.readonlyKeyCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a collection of all values stored in the Dictionary</summary>
|
||||||
|
ICollection IDictionary.Values {
|
||||||
|
get {
|
||||||
|
if(this.readonlyValueCollection == null) {
|
||||||
|
this.readonlyValueCollection = new ReadOnlyCollection<ValueType>(
|
||||||
|
this.typedDictionary.Values
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.readonlyValueCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing is not supported by the read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Assigning items is not supported by the read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Adding items is not supported by the read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the Dictionary</summary>
|
||||||
|
void ICollection<KeyValuePair<KeyType, ValueType>>.Clear() {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Clearing is not supported in a read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported in a read-only Dictionary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
/// <summary>ReadOnly wrapper for the keys collection of the Dictionary</summary>
|
||||||
|
private ReadOnlyCollection<KeyType> readonlyKeyCollection;
|
||||||
|
/// <summary>ReadOnly wrapper for the values collection of the Dictionary</summary>
|
||||||
|
private ReadOnlyCollection<ValueType> readonlyValueCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Collections
|
240
Source/Collections/ReadOnlyList.cs
Normal file
240
Source/Collections/ReadOnlyList.cs
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
/// <summary>Wraps a List and prevents users from modifying it</summary>
|
||||||
|
/// <typeparam name="ItemType">Type of items to manage in the List</typeparam>
|
||||||
|
public class ReadOnlyList<ItemType> :
|
||||||
|
IList<ItemType>,
|
||||||
|
IList {
|
||||||
|
|
||||||
|
/// <summary>Initializes a new read-only List wrapper</summary>
|
||||||
|
/// <param name="list">List that will be wrapped</param>
|
||||||
|
public ReadOnlyList(IList<ItemType> list) {
|
||||||
|
this.typedList = list;
|
||||||
|
this.objectList = (list as IList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Retrieves the index of an item within the List</summary>
|
||||||
|
/// <param name="item">Item whose index will be returned</param>
|
||||||
|
/// <returns>The zero-based index of the specified item in the List</returns>
|
||||||
|
public int IndexOf(ItemType item) {
|
||||||
|
return this.typedList.IndexOf(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Accesses the List item with the specified index</summary>
|
||||||
|
/// <param name="index">Zero-based index of the List item that will be accessed</param>
|
||||||
|
public ItemType this[int index] {
|
||||||
|
get { return this.typedList[index]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the List contains the specified item</summary>
|
||||||
|
/// <param name="item">Item that will be checked for</param>
|
||||||
|
/// <returns>True if the specified item is contained in the List</returns>
|
||||||
|
public bool Contains(ItemType item) {
|
||||||
|
return this.typedList.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Copies the contents of the List into an array</summary>
|
||||||
|
/// <param name="array">Array the List will be copied into</param>
|
||||||
|
/// <param name="arrayIndex">
|
||||||
|
/// Starting index at which to begin filling the destination array
|
||||||
|
/// </param>
|
||||||
|
public void CopyTo(ItemType[] array, int arrayIndex) {
|
||||||
|
this.typedList.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The number of items current contained in the List</summary>
|
||||||
|
public int Count {
|
||||||
|
get { return this.typedList.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the List is write-protected</summary>
|
||||||
|
public bool IsReadOnly {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a new enumerator over the contents of the List</summary>
|
||||||
|
/// <returns>The new List contents enumerator</returns>
|
||||||
|
public IEnumerator<ItemType> GetEnumerator() {
|
||||||
|
return this.typedList.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IList<> implementation
|
||||||
|
|
||||||
|
/// <summary>Inserts an item into the List</summary>
|
||||||
|
/// <param name="index">Zero-based index before which the item will be inserted</param>
|
||||||
|
/// <param name="item">Item that will be inserted into the List</param>
|
||||||
|
void IList<ItemType>.Insert(int index, ItemType item) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Inserting items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes an item from the list</summary>
|
||||||
|
/// <param name="index">Zero-based index of the item that will be removed</param>
|
||||||
|
void IList<ItemType>.RemoveAt(int index) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Accesses the List item with the specified index</summary>
|
||||||
|
/// <param name="index">Zero-based index of the List item that will be accessed</param>
|
||||||
|
ItemType IList<ItemType>.this[int index] {
|
||||||
|
get { return this.typedList[index]; }
|
||||||
|
set {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Assigning items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ICollection<> implementation
|
||||||
|
|
||||||
|
/// <summary>Adds an item to the end of the List</summary>
|
||||||
|
/// <param name="item">Item that will be added to the List</param>
|
||||||
|
void ICollection<ItemType>.Add(ItemType item) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Adding items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the List</summary>
|
||||||
|
void ICollection<ItemType>.Clear() {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Clearing is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes the specified item from the List</summary>
|
||||||
|
/// <param name="item">Item that will be removed from the List</param>
|
||||||
|
/// <returns>True of the specified item was found in the List and removed</returns>
|
||||||
|
bool ICollection<ItemType>.Remove(ItemType item) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IEnumerable implementation
|
||||||
|
|
||||||
|
/// <summary>Returns a new enumerator over the contents of the List</summary>
|
||||||
|
/// <returns>The new List contents enumerator</returns>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() {
|
||||||
|
return this.objectList.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IList implementation
|
||||||
|
|
||||||
|
/// <summary>Removes all items from the List</summary>
|
||||||
|
void IList.Clear() {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Clearing is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds an item to the end of the List</summary>
|
||||||
|
/// <param name="value">Item that will be added to the List</param>
|
||||||
|
int IList.Add(object value) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Adding items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the List contains the specified item</summary>
|
||||||
|
/// <param name="value">Item that will be checked for</param>
|
||||||
|
/// <returns>True if the specified item is contained in the List</returns>
|
||||||
|
bool IList.Contains(object value) {
|
||||||
|
return this.objectList.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Retrieves the index of an item within the List</summary>
|
||||||
|
/// <param name="value">Item whose index will be returned</param>
|
||||||
|
/// <returns>The zero-based index of the specified item in the List</returns>
|
||||||
|
int IList.IndexOf(object value) {
|
||||||
|
return this.objectList.IndexOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Inserts an item into the List</summary>
|
||||||
|
/// <param name="index">Zero-based index before which the item will be inserted</param>
|
||||||
|
/// <param name="value">Item that will be inserted into the List</param>
|
||||||
|
void IList.Insert(int index, object value) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Inserting items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the size of the List is fixed</summary>
|
||||||
|
bool IList.IsFixedSize {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes the specified item from the List</summary>
|
||||||
|
/// <param name="value">Item that will be removed from the List</param>
|
||||||
|
/// <returns>True of the specified item was found in the List and removed</returns>
|
||||||
|
void IList.Remove(object value) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes an item from the list</summary>
|
||||||
|
/// <param name="index">Zero-based index of the item that will be removed</param>
|
||||||
|
void IList.RemoveAt(int index) {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Removing items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Accesses the List item with the specified index</summary>
|
||||||
|
/// <param name="index">Zero-based index of the List item that will be accessed</param>
|
||||||
|
object IList.this[int index] {
|
||||||
|
get { return this.objectList[index]; }
|
||||||
|
set {
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Assigning items is not supported by the read-only List"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ICollection implementation
|
||||||
|
|
||||||
|
/// <summary>Copies the contents of the List into an array</summary>
|
||||||
|
/// <param name="array">Array the List 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.objectList.CopyTo(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the List is synchronized for multi-threaded usage</summary>
|
||||||
|
bool ICollection.IsSynchronized {
|
||||||
|
get { return this.objectList.IsSynchronized; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Synchronization root on which the List locks</summary>
|
||||||
|
object ICollection.SyncRoot {
|
||||||
|
get { return this.objectList.SyncRoot; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>The wrapped List under its type-safe interface</summary>
|
||||||
|
private IList<ItemType> typedList;
|
||||||
|
/// <summary>The wrapped List under its object interface</summary>
|
||||||
|
private IList objectList;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Collections
|
36
Source/Collections/ReverseComparer.cs
Normal file
36
Source/Collections/ReverseComparer.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares two values in reverse or reverses the output of another comparer
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="ComparedType">Type of values to be compared</typeparam>
|
||||||
|
public class ReverseComparer<ComparedType> : IComparer<ComparedType> {
|
||||||
|
|
||||||
|
/// <summary>Initializes a new reverse comparer</summary>
|
||||||
|
public ReverseComparer() : this(Comparer<ComparedType>.Default) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the comparer to provide the inverse results of another comparer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="comparerToReverse">Comparer whose results will be inversed</param>
|
||||||
|
public ReverseComparer(IComparer<ComparedType> comparerToReverse) {
|
||||||
|
this.comparer = comparerToReverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Compares the left value to the right value</summary>
|
||||||
|
/// <param name="left">Value on the left side</param>
|
||||||
|
/// <param name="right">Value on the right side</param>
|
||||||
|
/// <returns>The relationship of the two values</returns>
|
||||||
|
public int Compare(ComparedType left, ComparedType right) {
|
||||||
|
return this.comparer.Compare(right, left); // intentionally reversed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The default comparer from the .NET framework</summary>
|
||||||
|
private IComparer<ComparedType> comparer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Collections
|
|
@ -25,7 +25,7 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
/// <summary>Specialized memory stream for ring buffers</summary>
|
/// <summary>Specialized memory stream for ring buffers</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This ring buffer class is specialized for binary data and attempts to achieve
|
/// This ring buffer class is specialized for binary data and tries to achieve
|
||||||
/// optimal efficiency when storing and retrieving chunks of several bytes
|
/// optimal efficiency when storing and retrieving chunks of several bytes
|
||||||
/// at once. Typical use cases include audio and network buffers where one party
|
/// at once. Typical use cases include audio and network buffers where one party
|
||||||
/// is responsible for refilling the buffer at regular intervals while the other
|
/// is responsible for refilling the buffer at regular intervals while the other
|
||||||
|
@ -50,10 +50,11 @@ namespace Nuclex.Support.Collections {
|
||||||
get { return this.ringBuffer.Length; }
|
get { return this.ringBuffer.Length; }
|
||||||
set {
|
set {
|
||||||
int length = (int)Length;
|
int length = (int)Length;
|
||||||
if(value < length)
|
if(value < length) {
|
||||||
throw new ArgumentOutOfRangeException(
|
throw new ArgumentOutOfRangeException(
|
||||||
"New capacity is less than the stream's length"
|
"New capacity is less than the stream's current length"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// This could be done in a more efficient manner than just replacing
|
// This could be done in a more efficient manner than just replacing
|
||||||
// the entire buffer, but since this operation will probably be only called
|
// the entire buffer, but since this operation will probably be only called
|
||||||
|
@ -62,8 +63,9 @@ namespace Nuclex.Support.Collections {
|
||||||
MemoryStream newBuffer = new MemoryStream((int)value);
|
MemoryStream newBuffer = new MemoryStream((int)value);
|
||||||
|
|
||||||
newBuffer.SetLength(value);
|
newBuffer.SetLength(value);
|
||||||
if(length > 0)
|
if(length > 0) {
|
||||||
Read(newBuffer.GetBuffer(), 0, length);
|
Read(newBuffer.GetBuffer(), 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
this.ringBuffer.Close(); // Equals dispose of the old buffer
|
this.ringBuffer.Close(); // Equals dispose of the old buffer
|
||||||
this.ringBuffer = newBuffer;
|
this.ringBuffer = newBuffer;
|
||||||
|
@ -119,12 +121,13 @@ namespace Nuclex.Support.Collections {
|
||||||
this.ringBuffer.Read(buffer, offset, count);
|
this.ringBuffer.Read(buffer, offset, count);
|
||||||
this.startIndex += count;
|
this.startIndex += count;
|
||||||
|
|
||||||
if(this.startIndex == this.endIndex)
|
if(this.startIndex == this.endIndex) {
|
||||||
setEmpty();
|
setEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The end index lies before the start index, so the data in the
|
// The end index lies before the start index, so the data in the
|
||||||
// ring memory stream is fragmented. Example: |#####>-------<#####|
|
// ring memory stream is fragmented. Example: |#####>-------<#####|
|
||||||
} else {
|
} else {
|
||||||
int linearAvailable = (int)this.ringBuffer.Length - this.startIndex;
|
int linearAvailable = (int)this.ringBuffer.Length - this.startIndex;
|
||||||
|
|
||||||
|
@ -142,8 +145,8 @@ namespace Nuclex.Support.Collections {
|
||||||
this.startIndex = count - linearAvailable;
|
this.startIndex = count - linearAvailable;
|
||||||
this.ringBuffer.Read(buffer, offset + linearAvailable, this.startIndex);
|
this.ringBuffer.Read(buffer, offset + linearAvailable, this.startIndex);
|
||||||
|
|
||||||
// Nope, the amount of requested data can be read in one piece without
|
// Nope, the amount of requested data can be read in one piece without
|
||||||
// crossing the end of the ring buffer
|
// crossing the end of the ring buffer
|
||||||
} else {
|
} else {
|
||||||
this.ringBuffer.Position = this.startIndex;
|
this.ringBuffer.Position = this.startIndex;
|
||||||
this.ringBuffer.Read(buffer, offset, count);
|
this.ringBuffer.Read(buffer, offset, count);
|
||||||
|
@ -151,8 +154,9 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.startIndex == this.endIndex)
|
if(this.startIndex == this.endIndex) {
|
||||||
setEmpty();
|
setEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
@ -183,8 +187,8 @@ namespace Nuclex.Support.Collections {
|
||||||
this.endIndex = count - linearAvailable;
|
this.endIndex = count - linearAvailable;
|
||||||
this.ringBuffer.Write(buffer, offset + linearAvailable, this.endIndex);
|
this.ringBuffer.Write(buffer, offset + linearAvailable, this.endIndex);
|
||||||
|
|
||||||
// All data can be appended at the current stream position without
|
// All data can be appended at the current stream position without
|
||||||
// crossing the ring memory stream's end
|
// crossing the ring memory stream's end
|
||||||
} else {
|
} else {
|
||||||
this.ringBuffer.Position = this.endIndex;
|
this.ringBuffer.Position = this.endIndex;
|
||||||
this.ringBuffer.Write(buffer, offset, count);
|
this.ringBuffer.Write(buffer, offset, count);
|
||||||
|
@ -193,9 +197,9 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
this.empty = false;
|
this.empty = false;
|
||||||
|
|
||||||
// The end index lies before the start index, so the data in the ring memory
|
// The end index lies before the start index, so the data in the ring memory
|
||||||
// stream has been fragmented. This means the gap into which we are about
|
// stream has been fragmented. This means the gap into which we are about
|
||||||
// to write is not fragmented. Example: |#####>-------<#####|
|
// to write is not fragmented. Example: |#####>-------<#####|
|
||||||
} else {
|
} else {
|
||||||
if(count > (this.startIndex - this.endIndex))
|
if(count > (this.startIndex - this.endIndex))
|
||||||
throw new OverflowException("Data does not fit in buffer");
|
throw new OverflowException("Data does not fit in buffer");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user