List<T>类说明
如果说上次的ArrayList类是一个存储object类的vector,那么List从性质上说基本上等同于vector,只不过微软为它新加了一点点点点的方法,加到打开官方文档东西多到看不过来的程度。既然List算是ArrayList的泛化,这一次的代码基本上照抄了ArrayList的代码,只是加入了泛型的概念,逻辑上不变
创造IList接口
C++在创建一个类时会同时创建头文件和源文件,这让我养成了先把要实现的函数罗列一遍再开始实现的习惯。C#中的接口恰好满足了我这种习惯(不过C#的接口设计目的一定不是这样的,这个我后面会解释的),所以在实现List<T>类之前,先写一个IList<T>接口来罗列相关的属性和方法
public interface IList<T> : IEnumerable
{
int Count { get; }
int Capacity { get; }
T this[int index] { get; set; }
void Add(T item);
void AddRange(T[] array);
void Insert(int index, T list);
void InsertRange(int index, T[] array);
void Clear();
bool Contains(T item);
int IndexOf(T item);
void Remove(T item);
void RemoveAt(int index);
void Reserve();
T[] ToArray();
void TrimToSize();
}
我这样写接口的理由是完全错误的,接口的作用是规定调用者和被调用者,调用者只能使用接口中的方法,用于完成一定的逻辑,被调用者必须要实现相关的方法,以便调用者的使用。有一个很典型的例子:foreach
// 原始代码
foreach (var item in collection)
{
// 处理 item
}
// 等效编译结果
var enumerator = collection.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
var item = enumerator.Current;
// 处理 item
}
}
finally
{
(enumerator as IDisposable)?.Dispose(); // 清理资源
}
foreach规定collection必须实现IEnumerable,然后foreach只会调用IEnumerable中的方法,也就是Current,MoveNext(),Reset(),这就是一个很完美的接口实例,它体现了接口应该为实现某一种特定的逻辑功能而设计,换句话说,接口要小的恰好能满足一个功能的实现
既然这样,为什么我还要实现一个IList<T>接口呢,它能刚好用来实现一个什么功能呢,我觉得应该不行,它太大了。我觉得它应该是很多接口的集合,但我目前能力有限,不能从项目之初就准确地把它分成精妙的小组件,但是我想先放着,等到后续有什么想法的时候再来划分这个接口,还是那句话,先做完,再做好
List<T>类的实现
正如之前所说,List的很多方法基本就是把ArrayList的方法里的object改成T,所以除非有较大改动,不然就不做说明了
字段和属性
protected T[] array;
public int Count { get; protected set; }
public int Capacity { get; protected set; }
public T this[int index]
{
get
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException("index");
return array[index];
}
set
{
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException("index");
else if (index == Count)
Add(value);
else
array[index] = value;
}
}
上次ArrayList做完之后我发现再C#官方文档中把索引器作为一种属性,那之后我也把索引器放在这里,关于索引器,我也进行了较大的改动,之前没有考虑到index越界的问题,这次加上了,同时也增加了set方法中index==Count的情况,把它视为一种Add方法的调用,这样就可以写出以下这种代码了
List<int> list = new();
for (int i = 0; i < 100; i++)
list[i] = i;
会有人这样写代码吗,不知道,反正写着
构造器
基本不变
public List()
{
array = new T[10];
Capacity = 10;
Count = 0;
}
public List(int capacity)
{
array = new T[capacity];
Capacity = capacity;
Count = 0;
}
public List(T[] array)
{
int len = array.Length;
Capacity = (len / 10 + 1) * 10;
this.array = new T[Capacity];
Count = 0;
foreach (T item in array)
{
this.array[Count++] = item;
}
}
Add类方法
基本不变
protected void AddCapacity(int num = 1)
{
int newCapacity = Capacity * (int)Math.Pow(2, num);
T[] newArray = new T[newCapacity];
for (int i = 0; i < Count; i++)
newArray[i] = array[i];
Capacity = newCapacity;
array = newArray;
}
protected int CalcAddition(int len)
{
int times = (Count + len) / Capacity;
int count = 0;
while (times > 0)
{
times >>= 1;
count++;
}
return count;
}
public void Add(T item)
{
if (Count == Capacity)
AddCapacity();
array[Count++] = item;
}
public void AddRange(T[] array)
{
int len = array.Length;
int count = CalcAddition(len);
if (count != 0)
AddCapacity(count);
for (int i = 0; i < len; i++)
this.array[Count + i] = array[i];
Count += len;
}
public void Insert(int index, T item)
{
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException(nameof(index));
if (Count == Capacity)
AddCapacity();
for (int i = Count - 1; i >= index; i--)
array[i + 1] = array[i];
array[index] = item;
Count++;
}
public void InsertRange(int index, T[] array)
{
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException(nameof(index));
int len = array.Length;
int count = CalcAddition(len);
if (count != 0)
AddCapacity(count);
for (int i = Count - 1; i >= index; i--)
this.array[i + len] = this.array[i];
for (int i = 0; i < len; i++)
this.array[index + i] = array[i];
Count += len;
}
Remove类方法
基本不变
public void Clear()
{
Count = 0;
}
public void Remove(T value)
{
int index = IndexOf(value);
if (index == -1)
return;
else
{
for (int i = index; i < Count - 1; i++)
array[i] = array[i + 1];
Count--;
}
}
public void RemoveAt(int index)
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException();
for (int i = index; i < Count - 1; i++)
array[i] = array[i + 1];
Count--;
}
其他方法
IndexOf方法与之前变化较大,因为之前是object类型可以直接用array[i] == value,但是换成了泛型T,无法保证T也实现了==操作符的重载,不能直接写==,否则编译器会报错。这个报错是非常有帮助的,C++是没用这个报错的,大一我写自定义vector的时候就是直接用的==,然后写大作业的使用直接引用了那个类,而且T也是一个很复杂的自定义的类型,之后运行时报错,我为了抓这只虫子debug了一下午,如果能在写自定义vector类时注意到这一点会方便很多
C#的所有类都是由object派生的,且object类型有一个虚函数Equals,以下的代码意思是当T类型有重写这个方法时会调用重写的方法,否则调用object的方法,该函数有很好的兼容性
public int IndexOf(T value)
{
var comparer = EqualityComparer<T>.Default;
for (int i = 0; i < Count; i++)
{
if (comparer.Equals(array[i], value))
return i;
}
return -1;
}
Reserve方法也进行了一定的修改,我发现了一个很好用的语法糖,能够方便的实现两个元素的交换
public void Reserve()
{
int left = 0;
int right = Count - 1;
while (left < right)
{
(array[right], array[left]) = (array[left], array[right]);
left++;
right--;
}
}
其余的方法基本不变
public bool Contains(T value)
{
if (IndexOf(value) < 0)
return false;
else
return true;
}
public T[] ToArray()
{
T[] array = new T[Count];
int count = 0;
foreach (T item in this.array)
array[count++] = item;
return array;
}
public void TrimToSize()
{
if (Count != Capacity)
{
T[] newArray = new T[Count];
int count = 0;
foreach (T item in array)
newArray[count++] = item;
Capacity = count;
array = newArray;
}
}
virtual public List<T> GetRange(int start, int len = Int32.MaxValue)
{
if (start < 0 || start >= Count || len <= 0)
throw new ArgumentOutOfRangeException();
List<T> array = new List<T>();
int count = 0;
while (count < len && (count + start) < Count)
array.Add(this.array[start + count]);
return array;
}
public IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
public class Enumerator : IEnumerator
{
private readonly T[] array;
private readonly int arrSize;
private int count;
public Enumerator(List<T> list)
{
array = list.array;
arrSize = list.Count;
count = -1;
}
public object Current => array[count];
public bool MoveNext()
{
if (++count < arrSize)
return true;
return false;
}
public void Reset()
{
count = -1;
}
}
总结
List类是对ArrayList类的泛化,同时我也修改了相关的逻辑,整体看起来更工整,后续可能不知道什么时候我会把ArrayList类再拿出来改一改。完成了List类后,可以用List派生ArrayList类
public class ArrayList : List<object>
{
public ArrayList() { }
public ArrayList(int capacity) : base(capacity) { }
public ArrayList(object[] array) : base(array) { }
override public ArrayList GetRange(int start, int len = Int32.MaxValue)
{
if(start < 0 || start >= Count || len <= 0)
throw new ArgumentOutOfRangeException();
ArrayList array = new ArrayList();
int count = 0;
while(count < len && (count + start) < Count)
array.Add(this.array[start + count]);
return array;
}
}
这样再看ArrayList我就满意多了
571

被折叠的 条评论
为什么被折叠?



