题目
先来看上一篇中留下的作业题目:
写下一个名为MyList的类,仿造List类,并实现以下功能:
- 在构造方法中传入初始元素(传入元素的数量不定)
- 实现索引器索引功能,且在索引超出范围时抛出异常,如throw new ArgumentOutOfRangeException("索引参数超出范围");
- 拥有Count和Capacity两个属性,分别代表元素个数和数组长度,其中Count只读,Capacity只能增大,变小则抛出异常,如第2条,作用域都为public
- 实现Add,Insert,Sort,RemoveAt,IndexOf,LastIndexOf,Remove,作用域都为public
- 在Add,Insert中实现扩容机制
- 重载==运算符,要求有两个MyList<T>类型的参数,实现当两个MyList元素数量和内容相同时返回true,否则返回false,同时重载!=运算符
- 拥有两个字段,一个是data,用于作为数组存元素;一个是count,作为元素个数使用,作用域都为private
注意事项如下:
- 扩容时注意Count和Capacity元素修改
- 在Add,Insert,RemoveAt,Remove中注意Count元素修改
- 对于无法使用==时,可以使用a.Equals(b)判断a和b是否相等
- 对于!=重载,取==的反义即可
- 建议在类内大多情况下使用属性,而非私有字段
接下来我们一步步写下代码:
代码框架
首先,我们创建一个类MyList。
using System;
namespace Csharp
{
class MyList
{
}
class Program
{
static void Main(string[] args)
{
}
}
}
然后加入字段data(存储数组)和count(元素个数),以及属性Count(元素个数)和Capacity(数组长度)。
并为MyList添加一个泛型T,用于标识data数组的元素类型。
Count只需get无需set,这能使得元素个数变为只读属性。
记得给两个私有字段设置初始值!
using System;
namespace Csharp
{
class MyList<T>
{
private T[] data = new T[0];
private int count = 0;
public int Count
{
get
{
}
}
public int Capacity
{
get
{
}
set
{
}
}
}
class Program
{
static void Main(string[] args)
{
}
}
}
再添加上构造方法和所有需要实现的方法:
using System;
namespace Csharp
{
class MyList
{
private T[] data;
private int count;
public int Count
{
get
{
}
}
public int Capacity
{
get
{
}
set
{
}
}
public MyList()
{
}
public void Add()
{
}
public void Insert()
{
}
public void Sort()
{
}
public void RemoveAt()
{
}
public void IndexOf()
{
}
public void LastIndexOf()
{
}
public void Remove()
{
}
}
class Program
{
static void Main(string[] args)
{
}
}
}
框架定好了,接下来就是实现的问题了。
[属性]Count
由于Count是只读的,我们只需要返回字段count即可。
public int Count
{
get
{
return count;
}
}
[属性]Capacity
Capacity作为数组长度,逻辑是get返回data.Length,set则检查value是否大于Count,是则扩容,否则检查是否等于,等于则返回(什么也不做),否则就是小于了,抛出错误。
public int Capacity
{
get
{
return data.Length;
}
set
{
if (Count > value)
{
throw new ArgumentOutOfRangeException("容量不得设置小于元素个数");
}
if (Count == value)
{
return;
}
if (Count < value)
{
T[] newData = new T[value];
for (int i = 0; i < value; i++)
{
newData[i] = data[i];
}
data = newData;
}
}
}
[方法]Add
首先,参数为要添加的元素,我们设为item,类型为T。
然后,如果添加前元素个数(Count)和数组长度(Capacity)相同,那么添加后长度就会不足,所以需要扩容,具体为新建一个长度更长的数组,并把原数组的元素一个个复制到新数组,再在新数组中添加元素,最后把新数组赋值给数组。
public void Add(T item)
{
if (Capacity == Count)
{
T[] new_data = new T[Capacity*2];
for (int i = 0; i < Capacity; i++)
{
new_data[i] = data[i];
}
data = new_data;
}
data[Count] = item;
count++;
}
[构造方法]MyList
我们只需要设定一个参数数组,然后把参数数组中的元素一个个Add进来就可以了,甚至什么都不需要考虑,非常变量,这也就是为什么要先写Add。
public MyList(params T[] oris)
{
foreach(T i in oris)
{
Add(i);
}
}
[方法]Insert
Insert,意为插入,我们自然只能插入在原有的元素之间,所以要检查插入位置是否正确。
有两个参数,index代表插入位置,item代表插入元素,我们可以先把插入位置往后的元素都后移,然后直接将目标位置替换为插入元素。
注意,插入前要检查是否需要扩容!
public void Inset(int index, T item)
{
if (index<0 || index >= Count)
{
throw new ArgumentException("索引参数超出范围");
}
if (data.Length == Count)
{
T[] new_data = new T[Capacity*2];
for (int i = 0; i < Capacity; i++)
{
new_data[i] = data[i];
}
data = new_data;
}
for (int i = Count - 1; i > index - 1; i--)
{
data[i+1] = data[i];
}
data[index] = item;
count++;
}
[方法]RemoveAt
很简单,先检查是否索引超出范围,如果没超出范围,则将目标索引后面的元素都往前一格,即可覆盖掉原元素,达到删除的目的。
public void RemoveAt(int index)
{
if (index<0 || index >= Count)
{
throw new ArgumentException("索引参数超出范围");
}
for (int i = index + 1; i < Count; i++)
{
data[i - 1] = data[i];
}
count--;
}
[方法]IndexOf
一个一个遍历过去,然后检查一下,每一个元素是否与目标元素相同,相同则返回索引。
如果都不符合,则返回-1,这样用户才知道有没有符合的元素!
public int IndexOf(T item)
{
int index = -1;
for (int i = 0; i < Count; i++)
{
if (item.Equals(data[i]))
{
index = i;
break;
}
}
return index;
}
[方法]LastIndexOf
同上,把for循环顺序改为倒序即可。
public int LastIndexOf(T item)
{
int index = -1;
for (int i = Count - 1; i >= 0; i--)
{
if (item.Equals(data[i]))
{
index = i;
break;
}
}
return index;
}
[方法]Remove
我们需要做到从左到右遍历寻找,并删除对应位置,正好IndexOf和RemoveAt这样个方法可以完成全套流程,我们便可以这样写:
public void Remove(T item)
{
RemoveAt(IndexOf(item));
}
[方法]Sort
我们不要求很难的排序,可以直接用Array.Sort,前面讲过的。
public void Sort()
{
Array.Sort(data, 0, count); // 排序数组 开始位置 排序个数
}
[索引器]
首先检查目标索引有没有超出范围,再对data进行操作。
public T this[int index]
{
get
{
if (index < 0 || index >= count)
{
throw new ArgumentOutOfRangeException("索引参数超出范围");
}
return data[index];
}
set
{
if (index < 0 || index >= count)
{
throw new ArgumentOutOfRangeException("索引参数超出范围");
}
data[index] = value;
}
}
[重载运算符]==
先检查元素个数是否相同,再依次检查每个元素是否对应相同。(注意,此处用了Object.Equals方法,对于值对象,比较的是值本身,对于引用对象,比较的是引用地址)
public static bool operator==(MyList<T> list1, MyList<T> list2)
{
if (list1.Count != list2.Count)
{
return false;
}
for (int i = 0; i < list1.Count; i++)
{
if (!list1[i].Equals(list2[i]))
{
return false;
}
}
return true;
}
[重载运算符]!=
在重载==的同时,我们必须重载!=,只需要对==的结果取反即可。
public static bool operator!=(MyList<T> list1, MyList<T> list2)
{
return !(list1==list2);
}
最终代码
最后,代码如下:
class MyList<T>
{
private T[] data;
private int count;
public int Count
{
get
{
return count;
}
}
public int Capacity
{
get
{
return data.Length;
}
set
{
if (Count > value)
{
throw new ArgumentOutOfRangeException("容量不得设置小于元素个数");
}
if (Count == value)
{
return;
}
if (Count < value)
{
T[] newData = new T[value];
for (int i = 0; i < value; i++)
{
newData[i] = data[i];
}
data = newData;
}
}
}
public MyList(params T[] oris)
{
foreach(T i in oris)
{
Add(i);
}
}
public T this[int index]
{
get
{
if (index < 0 || index >= count)
{
throw new ArgumentOutOfRangeException("索引参数超出范围");
}
return data[index];
}
set
{
if (index < 0 || index >= count)
{
throw new ArgumentOutOfRangeException("索引参数超出范围");
}
data[index] = value;
}
}
public static bool operator==(MyList<T> list1, MyList<T> list2)
{
if (list1.Count != list2.Count)
{
return false;
}
for (int i = 0; i < list1.Count; i++)
{
if (!list1[i].Equals(list2[i]))
{
return false;
}
}
return true;
}
public static bool operator!=(MyList<T> list1, MyList<T> list2)
{
return !(list1==list2);
}
public void Add(T item)
{
if (Capacity == Count)
{
T[] new_data = new T[Capacity*2];
for (int i = 0; i < Capacity; i++)
{
new_data[i] = data[i];
}
data = new_data;
}
data[Count] = item;
count++;
}
public void Inset(int index, T item)
{
if (index<0 || index >= Count)
{
throw new ArgumentException("索引参数超出范围");
}
if (data.Length == Count)
{
T[] new_data = new T[Capacity*2];
for (int i = 0; i < Capacity; i++)
{
new_data[i] = data[i];
}
data = new_data;
}
for (int i = Count - 1; i > index - 1; i--)
{
data[i+1] = data[i];
}
data[index] = item;
count++;
}
public void Sort()
{
Array.Sort(data, 0, count); // 排序数组 开始位置 排序个数
}
public void RemoveAt(int index)
{
if (index<0 || index >= Count)
{
throw new ArgumentException("索引参数超出范围");
}
for (int i = index + 1; i < Count; i++)
{
data[i - 1] = data[i];
}
count--;
}
public int IndexOf(T item)
{
int index = -1;
for (int i = 0; i < Count; i++)
{
if (item.Equals(data[i]))
{
index = i;
break;
}
}
return index;
}
public int LastIndexOf(T item)
{
int index = -1;
for (int i = Count - 1; i >= 0; i--)
{
if (item.Equals(data[i]))
{
index = i;
break;
}
}
return index;
}
public void Remove(T item)
{
RemoveAt(IndexOf(item));
}
}
最后
面向对象初阶到这篇为止就结束了,如果你前面都理解了,一定能很轻松地解决这个问题。
下一篇Action委托、Func委托、多播委托、匿名方法和Lambda表达式!

Comments NOTHING