类代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace DevGuideToCollections
{
/// <summary>
/// Represents a strongly typed array.
/// </summary>
/// <typeparam name="T">Specifies the type of elements in the array.</typeparam>
[DebuggerDisplay("Count={Count}")]
[DebuggerTypeProxy(typeof(ArrayDebugView))]
public class ArrayEx<T>
{
const int GROW_BY = 10;
// Internal variable for holding the array information
T[] m_data;
// Contains the number of elements in the array.
int m_count;
int m_updateCode;
/// <summary>
/// Initializes a new instance of the ArrayEx(T) class that is empty.
/// </summary>
public ArrayEx()
{
Initialize(GROW_BY);
}
/// <summary>
/// Initializes a new instance of the ArrayEx(T) class that contains the items in the array.
/// </summary>
/// <param name="items">Adds items to the ArrayEx(T).</param>
public ArrayEx(IEnumerable<T> items)
{
Initialize(GROW_BY);
foreach (T item in items)
{
Add(item);
}
}
/// <summary>
/// Initializes a new instance of the ArrayEx(T) class that is empty and has the specified initial capacity.
/// </summary>
/// <param name="capacity">The number of elements that the new array can initially store.</param>
public ArrayEx(int capacity)
{
Initialize(capacity);
}
void Initialize(int capacity)
{
m_data = new T[capacity];
}
/// <summary>
/// States if the ArrayEx(T) is empty.
/// </summary>
public bool IsEmpty
{
get { return m_count <= 0; }
}
/// <summary>
/// Gets the number of elements actually contained in the ArrayEx(T).
/// </summary>
public int Count
{
get { return m_count; }
}
/// <summary>
/// Gets or sets the size of the internal data array.
/// </summary>
public int Capacity
{
get { return m_data.Length; }
set
{
// We do not support truncating the stored array. So throw an exception if the array is less than Count.
if (value < Count)
{
throw new ArgumentOutOfRangeException("value", "The value is less than Count");
}
// We do not need to do anything if the newly specified capacity is the same as the old one.
if (value == Capacity)
{
return;
}
// We will need to create a new array and move all of the values in the old array to the new one
T[] tmp = new T[value];
for (int i = 0; i < Count; ++i)
{
tmp[i] = m_data[i];
}
m_data = tmp;
++m_updateCode;
}
}
/// <summary>
/// Adds an object to the end of the ArrayEx(T).
/// </summary>
/// <param name="item">The item to add to the end of the ArrayEx(T).</param>
public void Add(T item)
{
if (m_data.Length <= m_count)
{
Capacity += GROW_BY;
}
// We will need to assign the item to the last element and then increment the count variable
m_data[m_count++] = item;
++m_updateCode;
}
/// <summary>
/// Checks to see if the item is present in the ArrayEx(T).
/// </summary>
/// <param name="item">The item to see if the array contains.</param>
/// <returns>True if the item is in the array, false if it is not.</returns>
public bool Contains(T item)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < m_count; i++)
{
if (comparer.Equals(m_data[i], item))
{
return true;
}
}
return false;
}
/// <summary>
/// Gets the index of the specified item.
/// </summary>
/// <param name="item">The item to get the index of.</param>
/// <returns>-1 if the item isn't found in the array, the index of the found item otherwise.</returns>
public int IndexOf(T item)
{
return Array.IndexOf<T>(m_data, item, 0, m_count);
}
/// <summary>
/// Clears all values from the ArrayEx(T).
/// </summary>
public void Clear()
{
Array.Clear(m_data, 0, m_count);
++m_updateCode;
m_count = 0;
}
/// <summary>
/// Removes the first occurrence of the specified item from the ArrayEx(T).
/// </summary>
/// <param name="item">The item to remove from the ArrayEx(T).</param>
/// <returns>True if an item was removed, false otherwise.</returns>
public bool Remove(T item)
{
return Remove(item, false);
}
/// <summary>
/// Removes the first or all occurrences of the specified item from the ArrayEx(T).
/// </summary>
/// <param name="item">The item to remove from the ArrayEx(T).</param>
/// <param name="alloccurrences">True if all occurrences of the item should be removed, False if only the first should be removed.</param>
/// <returns>True if an item was removed, false otherwise.</returns>
public bool Remove(T item, bool alloccurrences)
{
int shiftto = 0;
bool shiftmode = false;
bool removed = false;
int count = m_count;
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < count; ++i)
{
if (comparer.Equals(m_data[i], item) && (alloccurrences || !shiftmode))
{
// Decrement the count since we have found an instance
--m_count;
removed = true;
// Check to see if we have already found one occurrence of the item we are removing
if (!shiftmode)
{
// We will start shifting to the position of the first occurrence.
shiftto = i;
// Enable shifting
shiftmode = true;
}
continue;
}
if (shiftmode)
{
// Since we are shifting elements we need to shift the element down and then update the shiftto index to the next element.
m_data[shiftto++] = m_data[i];
}
}
for (int i = m_count; i < count; ++i)
{
m_data[i] = default(T);
}
if (removed)
{
++m_updateCode;
}
return removed;
}
/// <summary>
/// Removes the item located at the specified index.
/// </summary>
/// <param name="index">The index of the item to remove</param>
public void RemoveAt(int index)
{
if (index < 0 || index >= m_count)
{
// Item has already been removed.
return;
}
int count = Count;
// Shift all of the elements after the specified index down one.
for (int i = index + 1; i < count; ++i)
{
m_data[i - 1] = m_data[i];
}
// Decrement the count to reflect the item being removed.
--m_count;
++m_updateCode;
m_data[m_count] = default(T);
}
/// <summary>
/// Inserts an item into the ArrayEx(T) at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
if (index < 0 || index >= m_count)
{
throw new ArgumentOutOfRangeException("index");
}
if (m_count + 1 >= Capacity)
{
Capacity = m_count + GROW_BY;
}
// First we need to shift all elements at the location up by one
for (int i = m_count; i > index && i > 0; --i)
{
m_data[i] = m_data[i - 1];
}
m_data[index] = item;
++m_count;
++m_updateCode;
}
/// <summary>
/// Gets or sets an element in the ArrayEx(T).
/// </summary>
/// <param name="index">The index of the element.</param>
/// <returns>The value of the element.</returns>
public T this[int index]
{
get
{
if (index < 0 || index >= m_count)
{
throw new ArgumentOutOfRangeException("index");
}
return m_data[index];
}
set
{
if (index < 0 || index >= m_count)
{
throw new ArgumentOutOfRangeException("index");
}
m_data[index] = value;
++m_updateCode;
}
}
/// <summary>
/// Copies the elements of the ArrayEx(T) to a new array.
/// </summary>
/// <returns>An array containing copies of the elements of the ArrayEx(T).</returns>
public T[] ToArray()
{
T[] tmp = new T[Count];
for (int i = 0; i < Count; ++i)
{
tmp[i] = m_data[i];
}
return tmp;
}
}
}
测试方法
public static void TestArrayEx()
{
// Check null indexing
ArrayEx<ArrayEx<int>> nullableList = new ArrayEx<ArrayEx<int>>();
nullableList = new ArrayEx<ArrayEx<int>>();
ArrayEx<int> tmpList = new ArrayEx<int>();
nullableList.Add(new ArrayEx<int>());
nullableList.Add(null);
nullableList.Add(new ArrayEx<int>());
nullableList.Add(null);
nullableList.Add(tmpList);
nullableList.Add(null);
System.Diagnostics.Debug.Assert(nullableList.Contains(null));
System.Diagnostics.Debug.Assert(nullableList.Contains(tmpList));
System.Diagnostics.Debug.Assert(!nullableList.Contains(new ArrayEx<int>()));
ArrayEx<int> list = new ArrayEx<int>();
// Testing the add
list.Add(1);
list.Add(3);
list.Add(4);
list.Add(6);
list.Add(9);
list.Add(5);
list.Add(6);
list.Add(9);
list.Add(9);
list.Add(7);
List<int> tt = new List<int>(new int[] {1, 3, 4}) ;
System.Diagnostics.Debug.Assert(list.Count == 10);
// Testing the grow by
list.Add(14);
list.Add(19);
System.Diagnostics.Debug.Assert(list.Count == 12);
// Deleting the first 6 from the list
list.Remove(6, false);
System.Diagnostics.Debug.Assert(list.Contains(6));
System.Diagnostics.Debug.Assert(list.IndexOf(6) == 5);
System.Diagnostics.Debug.Assert(list.Count == 11);
// Deleting all 9s from the list
list.Remove(9, true);
System.Diagnostics.Debug.Assert(!list.Contains(9));
System.Diagnostics.Debug.Assert(list.Count == 8);
// Inserting a two at the 2nd position
list.Insert(1, 2);
System.Diagnostics.Debug.Assert(list[1] == 2);
System.Diagnostics.Debug.Assert(list[2] == 3);
System.Diagnostics.Debug.Assert(list.Count == 9);
// Check the DebugView method
ArrayDebugView view = new ArrayDebugView(list);
object[] values = view.Items;
System.Diagnostics.Debug.Assert(values.Length == list.Count);
for (int i = 0; i < list.Count; ++i)
{
System.Diagnostics.Debug.Assert((int)values[i] == list[i]);
}
// Testing clear
list.Clear();
System.Diagnostics.Debug.Assert(list.Count == 0);
list.Add(66);
list.Add(99);
System.Diagnostics.Debug.Assert(list[0] == 66);
System.Diagnostics.Debug.Assert(list[1] == 99);
// Test removing
System.Diagnostics.Debug.Assert(list.Remove(66));
System.Diagnostics.Debug.Assert(!list.Remove(68));
// Prepare for RemoveAt test
list.Clear();
list.Add(0);
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Add(6);
list.Add(7);
System.Diagnostics.Debug.Assert(list.Count == 8);
// Test RemoveAt the end
list.RemoveAt(7);
System.Diagnostics.Debug.Assert(list.Count == 7);
System.Diagnostics.Debug.Assert(list.Contains(6));
System.Diagnostics.Debug.Assert(!list.Contains(7));
// Test RemoveAt the middle
list.RemoveAt(4);
System.Diagnostics.Debug.Assert(list.Count == 6);
System.Diagnostics.Debug.Assert(list.Contains(3));
System.Diagnostics.Debug.Assert(list.Contains(5));
System.Diagnostics.Debug.Assert(!list.Contains(4));
// Test RemoveAt the front
list.RemoveAt(0);
System.Diagnostics.Debug.Assert(list.Count == 5);
System.Diagnostics.Debug.Assert(list.Contains(1));
System.Diagnostics.Debug.Assert(!list.Contains(0));
}