366 lines
12 KiB
C#
366 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using JD.Collections.Details;
|
|
using JD.Collections.Utilities;
|
|
using JD.Kernel.StaticHelpers;
|
|
|
|
namespace JD.Collections.ObjectModel
|
|
{
|
|
/// <summary>
|
|
/// ReadOnlyCollectionBase is a base class that can be used to more easily implement the
|
|
/// generic ICollection<T> and non-generic ICollection interfaces for a read-only collection:
|
|
/// a collection that does not allow adding or removing elements.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>To use ReadOnlyCollectionBase as a base class, the derived class must override
|
|
/// the Count and GetEnumerator methods. </para>
|
|
/// <para>ICollection<T>.Contains need not be implemented by the
|
|
/// derived class, but it should be strongly considered, because the ReadOnlyCollectionBase implementation
|
|
/// may not be very efficient.</para>
|
|
/// </remarks>
|
|
/// <typeparam name="T">The item type of the collection.</typeparam>
|
|
[Serializable]
|
|
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(ReadOnlyCollectionBase<>.DebugView))]
|
|
public abstract class ReadOnlyCollectionBase<T> : ICollection<T>, ICollection
|
|
{
|
|
#region Inner Types
|
|
|
|
private sealed class DebugView
|
|
{
|
|
#region Variables
|
|
|
|
private readonly ICollection<T> m_collection;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="collection"></param>
|
|
public DebugView(ICollection<T> collection)
|
|
{
|
|
Throw.IfNull(collection, "collection");
|
|
m_collection = collection;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Items.
|
|
/// </summary>
|
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
|
public T[] Items
|
|
{
|
|
get
|
|
{
|
|
T[] array = new T[m_collection.Count];
|
|
m_collection.CopyTo(array, 0);
|
|
return array;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Throws an NotSupportedException stating that this collection cannot be modified.
|
|
/// </summary>
|
|
protected virtual void MethodModifiesCollection()
|
|
{
|
|
throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, GetSimpleClassName(GetType())));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the simple name of the class, for use in exception messages.
|
|
/// </summary>
|
|
/// <returns>The simple name of this class.</returns>
|
|
private static string GetSimpleClassName(Type type)
|
|
{
|
|
string name = type.Name;
|
|
|
|
// Just use the simple name.
|
|
int index = name.IndexOfAny(new [] { '<', '{', '`' });
|
|
if (index >= 0)
|
|
{
|
|
name = name.Substring(0, index);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Overriden.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return new CollectionPrinter().Print(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICollection<T> Members
|
|
|
|
/// <summary>
|
|
/// This method throws an NotSupportedException
|
|
/// stating the collection is read-only.
|
|
/// </summary>
|
|
/// <param name="item">Item to be added to the collection.</param>
|
|
/// <exception cref="NotSupportedException">Always thrown.</exception>
|
|
void ICollection<T>.Add(T item)
|
|
{
|
|
MethodModifiesCollection();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method throws an NotSupportedException
|
|
/// stating the collection is read-only.
|
|
/// </summary>
|
|
/// <exception cref="NotSupportedException">Always thrown.</exception>
|
|
void ICollection<T>.Clear()
|
|
{
|
|
MethodModifiesCollection();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method throws an NotSupportedException
|
|
/// stating the collection is read-only.
|
|
/// </summary>
|
|
/// <param name="item">Item to be removed from the collection.</param>
|
|
/// <exception cref="NotSupportedException">Always thrown.</exception>
|
|
bool ICollection<T>.Remove(T item)
|
|
{
|
|
MethodModifiesCollection();
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the collection contains a particular item. This default implementation
|
|
/// iterates all of the items in the collection via GetEnumerator, testing each item
|
|
/// against <paramref name="item"/> using IComparable<T>.Equals or
|
|
/// Object.Equals.
|
|
/// </summary>
|
|
/// <remarks>You should strongly consider overriding this method to provide
|
|
/// a more efficient implementation.</remarks>
|
|
/// <param name="item">The item to check for in the collection.</param>
|
|
/// <returns>True if the collection contains <paramref name="item"/>, false otherwise.</returns>
|
|
public virtual bool Contains(T item)
|
|
{
|
|
IEqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
|
|
foreach (T i in this)
|
|
{
|
|
if (equalityComparer.Equals(i, item))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies all the items in the collection into an array. Implemented by
|
|
/// using the enumerator returned from GetEnumerator to get all the items
|
|
/// and copy them to the provided array.
|
|
/// </summary>
|
|
/// <param name="array">Array to copy to.</param>
|
|
/// <param name="arrayIndex">Starting index in <paramref name="array"/> to copy to.</param>
|
|
public virtual void CopyTo(T[] array, int arrayIndex)
|
|
{
|
|
int count = Count;
|
|
|
|
if (count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Throw.IfNull(array, "array");
|
|
Throw.ArgumentOutOfRangeIf(count < 0, "count", Strings.ArgMustNotBeNegative);
|
|
Throw.ArgumentOutOfRangeIf(arrayIndex < 0, "arrayIndex", Strings.ArgMustNotBeNegative);
|
|
Throw.InvalidArgumentIf(arrayIndex >= array.Length || count > array.Length - arrayIndex, "arrayIndex", Strings.ArrayTooSmall);
|
|
|
|
int index = arrayIndex, i = 0;
|
|
foreach (T item in (ICollection<T>)this)
|
|
{
|
|
if (i >= count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
array[index] = item;
|
|
++index;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an array of the correct size, and copies all the items in the
|
|
/// collection into the array, by calling CopyTo.
|
|
/// </summary>
|
|
/// <returns>An array containing all the elements in the collection, in order.</returns>
|
|
public virtual T[] ToArray()
|
|
{
|
|
int count = Count;
|
|
|
|
T[] array = new T[count];
|
|
CopyTo(array, 0);
|
|
return array;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Must be overridden to provide the number of items in the collection.
|
|
/// </summary>
|
|
/// <value>The number of items in the collection.</value>
|
|
public abstract int Count { get; }
|
|
|
|
/// <summary>
|
|
/// Indicates whether the collection is read-only. Returns the value
|
|
/// of readOnly that was provided to the constructor.
|
|
/// </summary>
|
|
/// <value>Always true.</value>
|
|
public bool IsReadOnly
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<T> Members
|
|
|
|
/// <summary>
|
|
/// Must be overridden to enumerate all the members of the collection.
|
|
/// </summary>
|
|
/// <returns>A generic IEnumerator<T> that can be used
|
|
/// to enumerate all the items in the collection.</returns>
|
|
public abstract IEnumerator<T> GetEnumerator();
|
|
|
|
#endregion
|
|
|
|
#region ICollection Members
|
|
|
|
/// <summary>
|
|
/// Copies all the items in the collection into an array. Implemented by
|
|
/// using the enumerator returned from GetEnumerator to get all the items
|
|
/// and copy them to the provided array.
|
|
/// </summary>
|
|
/// <param name="array">Array to copy to.</param>
|
|
/// <param name="index">Starting index in <paramref name="array"/> to copy to.</param>
|
|
void ICollection.CopyTo(Array array, int index)
|
|
{
|
|
int count = Count;
|
|
|
|
if (count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Throw.IfNull(array, "array");
|
|
Throw.ArgumentOutOfRangeIf(count < 0, "count", Strings.ArgMustNotBeNegative);
|
|
Throw.ArgumentOutOfRangeIf(index < 0, "arrayIndex", Strings.ArgMustNotBeNegative);
|
|
Throw.InvalidArgumentIf(index >= array.Length || count > array.Length - index, "arrayIndex", Strings.ArrayTooSmall);
|
|
|
|
int i = 0;
|
|
foreach (object o in (ICollection)this)
|
|
{
|
|
if (i >= count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
array.SetValue(o, index);
|
|
++index;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether the collection is synchronized.
|
|
/// </summary>
|
|
/// <value>Always returns false, indicating that the collection is not synchronized.</value>
|
|
bool ICollection.IsSynchronized
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the synchronization object for this collection.
|
|
/// </summary>
|
|
/// <value>Always returns this.</value>
|
|
object ICollection.SyncRoot
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable Members
|
|
|
|
/// <summary>
|
|
/// Provides an IEnumerator that can be used to iterate all the members of the
|
|
/// collection. This implementation uses the IEnumerator<T> that was overridden
|
|
/// by the derived classes to enumerate the members of the collection.
|
|
/// </summary>
|
|
/// <returns>An IEnumerator that can be used to iterate the collection.</returns>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
foreach (T item in this)
|
|
{
|
|
yield return item;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Debugger display
|
|
|
|
/// <summary>
|
|
/// Display the contents of the collection in the debugger. This is intentionally private, it is called
|
|
/// only from the debugger due to the presence of the DebuggerDisplay attribute. It is similar
|
|
/// format to ToString(), but is limited to 250-300 characters or so, so as not to overload the debugger.
|
|
/// </summary>
|
|
/// <returns>The string representation of the items in the collection, similar in format to ToString().</returns>
|
|
internal string DebuggerDisplayString()
|
|
{
|
|
const int MAXLENGTH = 250;
|
|
|
|
System.Text.StringBuilder builder = new System.Text.StringBuilder();
|
|
|
|
builder.Append('{');
|
|
|
|
// Call ToString on each item and put it in.
|
|
bool firstItem = true;
|
|
foreach (T item in this)
|
|
{
|
|
if (builder.Length >= MAXLENGTH)
|
|
{
|
|
builder.Append(", ...");
|
|
break;
|
|
}
|
|
|
|
if (!firstItem)
|
|
builder.Append(", ");
|
|
|
|
if (ReferenceEquals(item, null))
|
|
builder.Append("null");
|
|
else
|
|
builder.Append(item.ToString());
|
|
|
|
firstItem = false;
|
|
}
|
|
|
|
builder.Append('}');
|
|
return builder.ToString();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|