通过.NET反编译工具可以查看到ArrayList内部的代码,发现ArrayList并非由链表实现,而是由一个不断扩容的数组对象组成。

下面模仿ArrayList写一个自己的MyArrayList。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Collections;

namespace 模仿动态数组

{

/// <summary>

/// 模拟ArrayList类

/// </summary>

class MyArrayList

{

/// <summary>

/// 初始化MyArraiList的新实例,用默认的初始值初始化动态数组

/// </summary>

public MyArrayList()

{

//将容量属性初始值设为0

Capacity = 0;

//实例化一个新的数组实例

arr = new object[Capacity];

//定义数组索引为0

Index = 0;

}

/// <summary>

/// 初始化MyArrayList, 用指针的容量初始化动态数组,如果参数小于0,则抛出异常

/// </summary>

/// <param name="capacity">指定要初始化动态数组的大小</param>

public MyArrayList(int capacity)

{

//判断传入的容量是否合法

if (capacity < 0)

{

//如果参数不合法,抛出异常

throw new ArgumentOutOfRangeException("capacity", "capacity must not less than zero!");

}

//如果参数合法,用指定的大小初始化数组

this.arr = new object[capacity];

}

/// <summary>

/// 成员属性:数组引用

/// </summary>

private object[] arr;

/// <summary>

/// 成员属性:数组当前指针

/// </summary>

public int Index { get; set; }

/// <summary>

/// 成员属性:数组容量

/// </summary>

public int Capacity { get; set; }

/// <summary>

/// 构造一个索引器

/// </summary>

/// <param name="index">指定的索引值</param>

/// <returns>返回当前索引下数组的值</returns>

public object this[int index]

{

get

{

return arr[index];

}

set

{

//判断参数是否合法

if (index < 0 || index >= Index)

{

//如果不合法,抛出异常

throw new ArgumentOutOfRangeException("index", "index must be not less than 0 and not biger than index");

}

//否则,赋值

arr[index] = value;

}

}

/// <summary>

/// 成员方法:添加元素

/// </summary>

/// <param name="value"></param>

public void Add(object value)

{

//判断当前索引是否小于数组长度

if (Index < arr.Length)

{

//如果小于数组长度,则添加

arr[Index++] = value;

}

else

{

//如果超出数组长度-1

Capacity = arr.Length * 2;

//创建一个比当前数组大两倍的数组

object[] newArr = new object[arr.Length*2];

//将就数组的数据拷贝到新数组中

Array.Copy(arr, newArr,arr.Length);

//就数组的引用指向新的数组

arr = newArr;

//添加元素

arr[Index++] = value;

}

}

}

}

MyArrayList list = new MyArrayList();

//往动态数组中添加对象

For (int I = 0; I < 10; i++)

{

List.Add(i);

}

//遍历

foreach (object val in list)

{

Console.WriteLine(val);

}

发现这里出错了,为什么.Net Framework里的ArrayList对象可以用foreach遍历,而我们自己写的却不行

通过CSDN的介绍可知,

Foreach()等同于如下代码

IEnumerator  tor = new list. GetEnumerator();

While (list.MoveNext())

{

Console.WriteLine(list.Current);

}

foreach内部实际上调用对象内部的GetEnumerator()方法将动态数组封装成迭代器

GetEnumerator()也就是工厂函数,通过传入的arr和count生成迭代器对象

而GetEnumerator()来自于 IEnumerable接口。因此要想让foreach遍历自己写的动态数组类,必须实现IEnumerable接口。

该接口中的GetEnumerator要求返回一个对象,而且返回的是对象的父类,所以必须写一个迭代器对象的类,并且实现了IEnumerator,这样就可以在外部通过IEnumerator类型的变量调用它指向它的子类中实现了的方法。

基于此,我们来实现这个接口,代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Collections;

namespace 模仿动态数组

{

/// <summary>

/// 模拟ArrayList类

/// </summary>

class MyArrayList:IEnumerable

{

/// <summary>

/// 初始化MyArraiList的新实例,用默认的初始值初始化动态数组

/// </summary>

public MyArrayList()

{

//将容量属性初始值设为0

Capacity = 0;

//实例化一个新的数组实例

arr = new object[Capacity];

//定义数组索引为0

Index = 0;

}

/// <summary>

/// 初始化MyArrayList, 用指针的容量初始化动态数组,如果参数小于0,则抛出异常

/// </summary>

/// <param name="capacity">指定要初始化动态数组的大小</param>

public MyArrayList(int capacity)

{

//判断传入的容量是否合法

if (capacity < 0)

{

//如果参数不合法,抛出异常

throw new ArgumentOutOfRangeException("capacity", "capacity must not less than zero!");

}

//如果参数合法,用指定的大小初始化数组

this.arr = new object[capacity];

}

/// <summary>

/// 成员属性:数组引用

/// </summary>

private object[] arr;

/// <summary>

/// 成员属性:数组当前指针

/// </summary>

public int Index { get; set; }

/// <summary>

/// 成员属性:数组容量

/// </summary>

public int Capacity { get; set; }

/// <summary>

/// 构造一个索引器

/// </summary>

/// <param name="index">指定的索引值</param>

/// <returns>返回当前索引下数组的值</returns>

public object this[int index]

{

get

{

return arr[index];

}

set

{

//判断参数是否合法

if (index < 0 || index >= Index)

{

//如果不合法,抛出异常

throw new ArgumentOutOfRangeException("index", "index must be not less than 0 and not biger than index");

}

//否则,赋值

arr[index] = value;

}

}

/// <summary>

/// 成员方法:添加元素

/// </summary>

/// <param name="value"></param>

public void Add(object value)

{

//判断当前索引是否小于数组长度

if (Index < arr.Length)

{

//如果小于数组长度,则添加

arr[Index++] = value;

}

else

{

//如果超出数组长度-1

Capacity = arr.Length * 2;

//创建一个比当前数组大两倍的数组

object[] newArr = new object[arr.Length*2];

//将就数组的数据拷贝到新数组中

Array.Copy(arr, newArr,arr.Length);

//就数组的引用指向新的数组

arr = newArr;

//添加元素

arr[Index++] = value;

}

}

/// <summary>

/// 返回一个迭代器对象

/// </summary>

/// <returns>返回一个迭代器对象</returns>

public IEnumerator GetEnumerator()

{

return new MyIEnumerator(arr, Index);

}

}

}

接着完成 迭代器对象的类

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Collections;

namespace 模仿动态数组

{

/// <summary>

/// 一个迭代器的类,用来处理实现了ICollection接口的数组类

/// </summary>

class MyIEnumerator:IEnumerator

{

private object[] arr;

private int count;

private int index;

/// <summary>

/// 初始化MyIEnumerator的新实例,以指定的arr和count(数组实际长度,非容量)

/// </summary>

/// <param name="arr"></param>

/// <param name="count"></param>

public MyIEnumerator(object[] arr, int count)

{

this.arr = arr;

this.count = count;

//当前指针在首位

this.index = -1;

}

/// <summary>

/// 成员属性(只读):返回当前指针指向的元素

/// </summary>

public object Current

{

get

{

return arr[index];

}

}

/// <summary>

/// 成员方法:移动指针,判断当前指针指向的数是否合法

/// </summary>

/// <returns></returns>

public bool MoveNext()

{

index++;

return index < count;

}

/// <summary>

/// 成员方法:将当前数组指针重置为第一个元素之前

/// </summary>

public void Reset()

{

index = -1;

}

}

}

好了,这时再用foreach遍历我们自己的数组,看到效果了吧。

MyArrayList list = new MyArrayList();

List.Add(1);

List.Add(2);

List.Add(3);

List.Add(4);

List.Add(5);

Foreach (object val in list)

{

Console.WriteLine(val);

}

输出

1

2

3

4

5

模仿.NET框架ArrayList写一个自己的动态数组类MyArrayList,揭示foreach实现原理的更多相关文章

  1. 用C#写一个函数,在一个数组中找出随意几个值相加等于一个值 与迭代器对比

    算法!用C#写一个函数,在一个数组中找出随意几个值相加等于一个值比如,数组{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}  要找出那些数相加等 ...

  2. WPF MVVM 写一个健壮的INotifyPropertyChanged基类

    当我们用MVVM的时候要实现INotifyPropertyChanged,如果你是基于.net4.5以下的framework(.net4.5已有新特性我这里就不说了) 你很可能会这么写 public ...

  3. java 写一个JSON解析的工具类

    上面是一个标准的json的响应内容截图,第一个红圈”per_page”是一个json对象,我们可以根据”per_page”来找到对应值是3,而第二个红圈“data”是一个JSON数组,而不是对象,不能 ...

  4. 基于vue框架手写一个notify插件,实现通知功能

    简单编写一个vue插件,当点击时触发notify插件,dom中出现相应内容并且在相应时间之后清除,我们可以在根组件中设定通知内容和延迟消失时间. 1. 基础知识 我们首先初始化一个vue项目,删除不需 ...

  5. 用node.js给C#写一个数据表的实体类生成工具

    虽然微软提供了T4模板,但是我感觉非常难用.哪儿比得上直接用脚本来写模板来的爽. 因为要给一个老项目做周边的工具,需要连接到数据库. 我习惯性用EntityFrameworkCore来做,因为毕竟从出 ...

  6. 写一个操作 .ini文件的类

    class IniHelp { private string iniPath; [DllImport("kernel32")] private static extern long ...

  7. 设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

    本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网 ...

  8. android 开发 写一个RecyclerView布局的聊天室,并且添加RecyclerView的点击事件

    实现思维顺序: 1.首先我们需要准备2张.9的png图片(一张图片为左边聊天泡泡,一个图片为右边的聊天泡泡),可以使用draw9patch.bat工具制作,任何图片导入到drawable中. 2.需要 ...

  9. 自己写一个java.lang.reflect.Proxy代理的实现

    前言 Java设计模式9:代理模式一文中,讲到了动态代理,动态代理里面用到了一个类就是java.lang.reflect.Proxy,这个类是根据代理内容为传入的接口生成代理用的.本文就自己写一个Pr ...

随机推荐

  1. 关于QImage和IplImage之间转换的实现

    在嵌入式系统中实现qt和opencv的处理,最基础的就是QImage和IplImage之间的转换.这样两者就可以进行一起使用图像数据,从而达到利用qt显示和利用opencv处理的功能. 下面我将贴出代 ...

  2. Makefile的编写及四个特殊符号的意义@、$@、$^、$

    https://www.cnblogs.com/sky-heaven/p/9450435.html Makefile一般的格式是: target:components rule 一.@ 这个符串通常用 ...

  3. react+ant design Breadcrumb面包屑组件

    import React from 'react'; import { Link }from 'react-router-dom'; import { Breadcrumb } from 'antd' ...

  4. es8中对string补白的方式

    //允许将空字符串或其他字符串添加到原始字符串的开头或结尾for(let i = 1; i < 32; i++) { if(i < 10) { console.log(`0{i}`) }e ...

  5. bugku flag在index里

    原题内容: http://120.24.86.145:8005/post/ Mark一下这道题,前前后后弄了两个多小时,翻了一下别的博主的wp感觉还是讲的太粗了,这里总结下自己的理解: 首先打开这道题 ...

  6. vs rdlc 设置Tablix 在新页面重复表头

    设置方法: 1.选中Tablix控件 2.点开三角形 3.选择高级模式 4.在行组 下 选择静态,然后看右边的属性 5.将属性设置为如下 就可以让Tablix控件实现在新页中带表头

  7. centos 7 添加swap

    [root@lab01 /]# cd / [root@lab01 /]# + records in + records out bytes ( MB/s [root@lab01 /]# free -m ...

  8. RAID 5+备份硬盘实验:mdadm

    *独立冗余磁盘阵列---RAID5* RAID5+备份盘: 把硬盘设备的数据奇偶校验信息保存到其他硬盘设备中.  RAID 5磁盘阵列组中数据的奇偶校验信息并不是单独保存到某一块硬盘设备中, 而是存储 ...

  9. 京东秒杀抢购的小脚本和chorme的一个开发者插件

    chorme开发者插件 下载源码包:https://github.com/gongjunhao/seckill/archive/master.zip 解压:seckill-master.zip 打开c ...

  10. django model 高级进阶

    十.model高级用法: 10.1 ORM映射: Object Relational Mapping: orm映射的任务: 将list ,object 的操作转化为sql语句,根据对象生成数据表,将查 ...