C#foreach 本质( 鸭子类型遍历)
探讨关于C#中Foreach的本质
要实现foreach需要满足什么条件?
只要类中实现类中的GetEnumerator()方法、MoveNext()方法、Current属性(俗称鸭子类型)都可以使用foreach进行遍历。
所以只要继承IEnumerable或IEnumerator、集合类、数组 等类都可以使用foreach遍历。
c#不要求实现IEnumerable/IEnumerable来使用foreach迭代数据类型。相反,编译器使用一个称为duck typing的概念;它查找GetEnumerator方法,该方法返回具有Current属性和MoveNext方法的类型
以下案例可以看出:编译器用鸭子类型这一概念。该案例编译器不报错,但是运行时候会出错,运行时进行类型检测,发现cars 类无法转化成IEnumerator接口。
通过4种foreach的用法来了解真面模
List<int> i = new() { 1, 1, 1, 2 };
int[] j = { 1, 1, 1, 2 };
//用法一
foreach (var item in i)
{
// item = 22; 错误的写法 item就是 enumerator.Current属性,该属性是只读的;
Console.Write(item);
}
//用法二
foreach (var item in j)
{
Console.Write(item);
}
//用法三
foreach (var item in new F())
{
Console.Write(item + ", "); // 1, 2, 3, 4, 5,
} class F
{
public IEnumerator<int> GetEnumerator()
{
for (var i = 0; i < 5; ++i)
{
yield return i;
}
}
}
上面代码通过ILspy的反编译IIL代码如下:
具体分析:
1、数组是静态的直接可以通过索引确定。集合类是不确定长度的所以不能通过索引方式。数组继承了IEnumerable接口,该接口要求返回IEnumerator
对象。
2、集合类实现了IEnumerator接口
3、迭代返回的对象也现实了IEnumerator接口。
通过以上三种方式的使用可知foreach主要用到类中的GetEnumerator()方法、MoveNext()、Current属性。那么我们是否可以推断出不需要继承IEnumerator
接口,只要实现这三个方法的类照样可以使用foreach。
为了验证这个想法,我自定义一个类来验证,代码如下:
使用方法4:
using System.Collections; CarFactory cars = new CarFactory();
foreach (var car in cars)
{
Console.WriteLine(((Car)car).Modle);
}
public class CarFactory
{
private Car[] carlist;
int position = -1;
//Create internal array in constructor.
public CarFactory()
{
carlist =new Car[2] { new Car { Modle = "长城" }, new Car { Modle = "红旗" }};
} public bool MoveNext()
{
position++;
return (position < carlist.Length);
}
public CarFactory GetEnumerator()
{
return this;
} public Car Current
{
get { return carlist[position]; }
}
} public class Car
{
public string Modle { get; set; }
} /*输出:
长城
红旗*/
反编译后如下:
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
CarFactory cars = new CarFactory();
CarFactory enumerator = cars.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
Car car = enumerator.Current;
Console.WriteLine(car.Modle);
}
}
finally
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
}
案例5、应用扩展方法 注入方法GetEnumerator()
//扩展方法 由于CarFactory类中没有GetEnumerator()方法,只能用扩展方法注入。 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace CExtention
{
static class CarsExtention
{
public static CarFactory GetEnumerator(this CarFactory carFactory) => carFactory;
}
} //主类 using System.Collections;
using CExtention;
CarFactory cars = new CarFactory();
foreach (var car in cars)
{
Console.WriteLine(((Car)car).Modle);
}
public class CarFactory
{
private Car[] carlist;
int position = -1;
//Create internal array in constructor.
public CarFactory()
{
carlist =new Car[2] { new Car { Modle = "长城" }, new Car { Modle = "红旗" }};
} public bool MoveNext()
{
position++;
return (position < carlist.Length);
} public Car Current
{
get { return carlist[position]; }
}
} public class Car
{
public string Modle { get; set; }
} /*输出:
长城
红旗*/
通过对以上使用方法的分析我们可以得出
Foreach 只要类中实现类中的GetEnumerator()方法、MoveNext()、Current属性。都可以使用foreach进行遍历。
总结:
.NET 本质论 - 了解 C# foreach 的内部工作原理和使用 yield 的自定义迭代器
C#foreach 本质( 鸭子类型遍历)的更多相关文章
- python 全栈开发,Day21(抽象类,接口类,多态,鸭子类型)
一.昨日复习 派生方法和派生属性 super 只有在子父类拥有同名方法的时候, 想使用子类的对象调用父类的方法时,才使用super super在类内 : super().方法名(arg1,..) 指名 ...
- day25 面向对象之多态和鸭子类型
1.封装方法 如何封装:给方法名称前面加上双下划线 # ATM 的取款功能 # 1.插入银行卡 2.输入密码 3.选择取款金额 4.取款 class ATM: def __insert_card(se ...
- 封装之property,多态,鸭子类型,classmethod与staticmethod
一.封装之Property prooerty是一种特殊的属性,访问时他会执行一段功能(函数)然后返回 '''BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属 ...
- python 接口(抽象) 多态,鸭子类型, 多继承原理(mro)
抽象类与接口类 接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数 ...
- python面向对象-封装-property-接口-抽象-鸭子类型-03
封装 什么是封装: # 将复杂的丑陋的隐私的细节隐藏到内部,对外提供简单的使用接口 或 # 对外隐藏内部实现细节,并提供访问的接口 为什么需要封装 1.为了保证关键数据的安全性 2.对外部隐藏内部的实 ...
- 面向对象相关概念与在python中的面向对象知识(魔法方法+反射+元类+鸭子类型)
面向对象知识 封装 封装的原理是,其成员变量代表对象的属性,方法代表这个对象的动作真正的封装是,经过深入的思考,做出良好的抽象(设计属性时用到),给出“完整且最小”的接口,并使得内部细节可以对外透明( ...
- 第7.3节 Python特色的面向对象设计:协议、多态及鸭子类型
Python是一种多态语言,其表现特征是:对象方法的调用方只管方法是否可调用,不管对象是什么类型,从而屏蔽不同类型对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化. 一. P ...
- 类型检查和鸭子类型 Duck typing in computer programming is an application of the duck test 鸭子测试 鸭子类型 指示编译器将类的类型检查安排在运行时而不是编译时 type checking can be specified to occur at run time rather than compile time.
Go所提供的面向对象功能十分简洁,但却兼具了类型检查和鸭子类型两者的有点,这是何等优秀的设计啊! Duck typing in computer programming is an applicati ...
- 面向对象—多态、鸭子类型(Day21)
编程原则java具有自己的编程原则和设计模式,不能多继承.python的编程原则:1.开放封闭原则:开放是对扩展是开放的,封闭是对修改是封闭的(已经写完的代码程序是不能修改的).2.依赖倒置原则:高层 ...
随机推荐
- ros实例_百度语音+图灵
1 百度语音模块 参考http://blog.csdn.net/u011118482/article/details/55001444 1.1 百度语音识别包 git clonehttps://git ...
- MobaXterm中文乱码问题
现在Xshell和SecureCRT都要收费,本着不用盗版的原则,同时需要标签管理session,快捷命令等功能,最后选择了MobaXterm. 但是使用后发现中文会乱码.后按照博客的方法,修改了终端 ...
- Web开发底层是Servlet
SpringMVC:是基于spring的一个框架,实际上就是spring的一个模块,专门是做web开发. 可以理解成servlet是一个升级 web开发底层是servlet,框架是在servlet基础 ...
- Vue3.2中的setup语法糖,保证你看的明明白白!
vue3.2 到底更新了什么? 根据原文内容的更新的内容主要有以下 5 块: 1.SSR:服务端渲染优化.@vue/server-renderer包加了一个ES模块创建, 与Node.js解耦,使在非 ...
- ES6之async与await
· async - await 是 Promise 和 Generator 的语法糖,目的只是为了让我们书写代码时更加流畅,增强代码的可读性. · async - await 是建立在Promise机 ...
- Java枚举类在生产环境中的使用方式
前言 Java枚举在项目中使用非常普遍,许多人在做项目时,一定会遇到要维护某些业务场景状态的时候,往往会定义一个常量类,然后添加业务场景相关的状态常量.但实际上,生产环境的项目中业务状态的定义大部 ...
- 如何使用 pytorch 实现 yolov3
前言 看了 Yolov3 的论文之后,发现这论文写的真的是很简短,神经网络的具体结构和损失函数的公式都没有给出.所以这里参考了许多前人的博客和代码,下面进入正题. 网络结构 Yolov3 将主干网络换 ...
- Java流程控制02:Scanner进阶
Scanner进阶使用 import java.util.Scanner;public class Demo04 { public static void main(String[] args ...
- 业务4P分析实践
原创不易,求分享.求一键三连 前言 假期继续思考BI未来的方向,其实常规的BI规划也不是不能出,比如公司BI看板建设.数据指标体系建设.业务线UE模型数据监控等,但这种不会超出预期的东西自然也能算技术 ...
- 可能用得上的UI控件
为了便于开发者打造各式各样的优秀App,UIKit框架提供了非常多功能强大又易用的UI控件以下列举一些在开发中可能用得上的UI控件: 红色表明最常用,蓝色代表一般,黑色代表几乎不用(这不是绝对的, ...