模仿.NET框架ArrayList写一个自己的动态数组类MyArrayList,揭示foreach实现原理
通过.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实现原理的更多相关文章
- 用C#写一个函数,在一个数组中找出随意几个值相加等于一个值 与迭代器对比
算法!用C#写一个函数,在一个数组中找出随意几个值相加等于一个值比如,数组{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20} 要找出那些数相加等 ...
- WPF MVVM 写一个健壮的INotifyPropertyChanged基类
当我们用MVVM的时候要实现INotifyPropertyChanged,如果你是基于.net4.5以下的framework(.net4.5已有新特性我这里就不说了) 你很可能会这么写 public ...
- java 写一个JSON解析的工具类
上面是一个标准的json的响应内容截图,第一个红圈”per_page”是一个json对象,我们可以根据”per_page”来找到对应值是3,而第二个红圈“data”是一个JSON数组,而不是对象,不能 ...
- 基于vue框架手写一个notify插件,实现通知功能
简单编写一个vue插件,当点击时触发notify插件,dom中出现相应内容并且在相应时间之后清除,我们可以在根组件中设定通知内容和延迟消失时间. 1. 基础知识 我们首先初始化一个vue项目,删除不需 ...
- 用node.js给C#写一个数据表的实体类生成工具
虽然微软提供了T4模板,但是我感觉非常难用.哪儿比得上直接用脚本来写模板来的爽. 因为要给一个老项目做周边的工具,需要连接到数据库. 我习惯性用EntityFrameworkCore来做,因为毕竟从出 ...
- 写一个操作 .ini文件的类
class IniHelp { private string iniPath; [DllImport("kernel32")] private static extern long ...
- 设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理
本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网 ...
- android 开发 写一个RecyclerView布局的聊天室,并且添加RecyclerView的点击事件
实现思维顺序: 1.首先我们需要准备2张.9的png图片(一张图片为左边聊天泡泡,一个图片为右边的聊天泡泡),可以使用draw9patch.bat工具制作,任何图片导入到drawable中. 2.需要 ...
- 自己写一个java.lang.reflect.Proxy代理的实现
前言 Java设计模式9:代理模式一文中,讲到了动态代理,动态代理里面用到了一个类就是java.lang.reflect.Proxy,这个类是根据代理内容为传入的接口生成代理用的.本文就自己写一个Pr ...
随机推荐
- UNION JOIN 连接表
使用UNION JOIN进行多表连接,与9.3节介绍的各种表的连接类型不同,它并不对表中的数据进行任何匹配处理,而只是把来自一个源表中的行与另一个源表中的行联合起来,生成的结果表中包括第一个表中的所有 ...
- JFrog推出全球首个支持混合云架构,端到端的通用DevOps平台 ——JFrog Platform
JFrog Platform,基于屡获殊荣的JFrog Artifactory制品仓库的独特能力,通过多合一的体验提供DevSecOps.CI / CD和软件分发的解决方案. 2020 ...
- 问题 B: 奇怪的电梯
问题 B: 奇怪的电梯 时间限制: 1 Sec 内存限制: 128 MB[命题人:admin] 题目描述 大楼的每一层楼都可以停电梯,而且第i层楼(1<=i<=N)上有一个数字Ki(0& ...
- Linux(Ubuntu)服务器是否安装ssh,使用xshell远程连接
1.查看 ssh 是否启动,如果有 sshd 说明已经启动 sudo ps -e | grep ssh 2.启动 ssh 服务 sudo service ssh start 3.如果第二步没有成功启动 ...
- QT(mingw) 编译 boost
参考链接 :https://www.cnblogs.com/zhangnianyong/p/6546712.html Qt为mingw 5.8.0.Boost为1.62.0. 1.安装qt-opens ...
- Unity Coroutine详解(一)
Unity 中协程是个非常强大的功能,其作用主要是用于游戏中的延时调用或者执行一连串的有时间间隔的事件流程,例如剧情对话等.简单总结了几点协程相关的知识点,旨在加深记忆,同时为初学者解惑. 1.协程. ...
- CentOS7 卸载Firefox
先进入管理员模式 执行: yum remove firefox 然后用whereis 查看,却发现还是有: [root@localhost ~]# whereis firefox firefox: / ...
- 神经网络的基础-Graph,Session
张量:基于 Tensorflow 的 NN:用张量表示数据,用计算图搭建神经网络,用会话执行计算图,优化线上的权重(参数),得到模型. 张量:张量就是多维数组(列表),用“阶”表示张量的维度. 0 阶 ...
- 概率dp poj 3071
题目首先给出一个n,表示比赛一共进行n轮,那么队伍就有2^n只队伍输入一个2^n*2^n的矩阵,p[i][j]代表队伍i打败队伍j的概率dp[i][j]代表第i轮比赛的时候,队伍j赢的概率首先初始化时 ...
- CPI和PPI,谁代表了通膨?
CPI.PPI 一则旧闻,根据 2016年1月9日 统计局公布的数据,CPI同比增长1.6%,PPI同比下降5.9%. Consumer Price Index 缩写CPI,居民消费价格指数. 统计范 ...