JD2022-TU1/main/tools/framework/JD.Collections/ObjectModel/ReadOnlyCollectionBase.cs

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&lt;T&gt; 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&lt;T&gt;.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&lt;T&gt;.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&lt;T&gt; 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&lt;T&gt; 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
}
}