C# 9.0新特性——扩展方法GetEnumerator支持foreach循环
作者:markkang 发布时间:2021-08-27 22:38:09
1.介绍
我们知道,我们要使一个类型支持foreach循环,就需要这个类型满足下面条件之一:
该类型实例如果实现了下列接口中的其中之一:
System.Collections.IEnumerable
System.Collections.Generic.IEnumerable<T>
System.Collections.Generic.IAsyncEnumerable<T>
该类型中有公开的无参GetEnumerator()方法,且其返回值类型必须是类,结构或者接口,同时返回值类型具有公共 Current 属性和公共无参数且返回类型为 Boolean的MoveNext 方法。
上面的第一个条件,归根结底还是第二个条件的要求,因为这几个接口,里面要求实现的还是GetEnumerator方法,同时,接口中GetEnumerator的返回值类型IEnumerator接口中要实现的成员和第二条中返回值类型的成员相同。
C#9.0之前,是不支持采取扩展方法的方式给类型注入GetEnumerator方法,以支持foreach循环的。从C#9.0之后,这种情况得到了支持。
2. 应用与示例
在这里,我们定义一个People类,它可以枚举其所有组员Person,并且在其中定义了MoveNext方法和Current属性。同时,我们也通过扩展方法给People注入了GetEnumerator方法。这样,我们就可以使用foreach来枚举People对象了。
首先,我们来定义一个Person记录:
public record Person(string FirstName, string LastName);
下来,我们来创建People类型,用来描述多个Person对象,并提供GetEnumerator返回值类型中所需的Current属性和MoveNext方法。在此,我们没有实现任何接口:
public class People:IDisposable//: IEnumerator<Person>
{
int position = -1;
private Person[] _people { get; init; }
public People(Person[] people)
{
_people = people;
}
public bool MoveNext()
{
position++;
return (position < _people.Length);
}
public Person Current
{
get
{
try
{
return _people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Reset()
{
position = -1;
}
public void Dispose()
{
Reset();
}
}
需要注意的是People中,由于没有通过使用前面的接口来实现支持foreach功能,这样就存在一个问题,就是第一次foreach循环完成后,状态还没有恢复到初始状态,第二次使用foreach进行枚举就没有可用项。因此我们添加了Reset方法用于手工恢复回初始状态,如果想让foreach能自动恢复状态,就让People实现接口IDisposable,并在其实现中,调用Reset方法。
然后,我们定义扩展方法,给People注入GetEnumerator方法
static class PeopleExtensions
{
//public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> people) => people;
public static People GetEnumerator(this People people) => people;
}
最后,只要引用了扩展方法所在的命名空间,foreach循环就可以使用了。
var PersonList = new Person[3]
{
new ("John", "Smith"),
new ("Jim", "Johnson"),
new ("Sue", "Rabon"),
};
var people = new People(PersonList);
foreach (var person in people)
{
Console.WriteLine(person);
}
到这里,我们就完成了利用扩展方法来实现foreach循环的示例,为了方便拷贝测试,我们所有的代码放在一起就如下所示:
var PersonList = new Person[3]
{
new ("John", "Smith"),
new ("Jim", "Johnson"),
new ("Sue", "Rabon"),
};
var people = new People(PersonList);
foreach (var person in people)
{
Console.WriteLine(person);
}
public record Person(string FirstName, string LastName);
public class People:IDisposable//: IEnumerator<Person>
{
int position = -1;
private Person[] _people { get; init; }
public People(Person[] people)
{
_people = people;
}
public bool MoveNext()
{
position++;
return (position < _people.Length);
}
public Person Current
{
get
{
try
{
return _people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Reset()
{
position = -1;
}
public void Dispose()
{
Reset();
}
}
static class PeopleExtensions
{
//public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> people) => people;
public static People GetEnumerator(this People people) => people;
}
结束语
解除原有的限制,扩展方法GetEnumerator支持foreach循环,为特殊的需要提供了一种可能。
作者:MarkKang
出处:https://www.cnblogs.com/markkang/
来源:https://www.cnblogs.com/markkang/p/14018990.html


猜你喜欢
- 本文实例讲述了C#同步网络时间的方法。分享给大家供大家参考。具体分析如下:客户的机器的系统时间经常出错,导致给他们做的软件无法正常使用,所以
- 前后端分离开发中,一般都会遇到请求跨域问题。而且一般也会遇到登陆失效问题。今天就以springboot和vue为例来看如何解决上述问题增加过
- 在SpringAMQP的发送方法中,接收消息的类型是Object,也就是说我们可以发送任意对象类型的消息,SpringAMQP会帮我们序列化
- 一、什么是深拷贝和浅拷贝对于所有面向对象的语言,复制永远是一个容易引发讨论的题目,C#中也不例外。此类问题在面试中极其容易被问到,我们应该在
- 本文实例为大家分享了UnityShader使用Plane实现翻书效果的具体代码,供大家参考,具体内容如下之前在网上看到一个Shadr可以实现
- 前言Java中共有八种基本数据类型:byte,int,short,long,float,double,char,boolean。计算机中的基
- 一、JAVA常用APIjava.lang.Math提供sin, cos, tan, exp, log, log10 等类方法,PI和E等类字
- 概述在JAVA JDK8 List分组的实现和用法一文中介绍了JDK 8如何对list进行分组,但是没有提到如何在分组后,获取每个分组的第一
- 面试中可能会被问到为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?Java 创建线程的方法实际
- Java裁剪压缩PNG图片,透明背景色变黑import java.awt.Graphics2D;import java.awt.Image;
- 定义一个对象应该对其他对象保持最少的了解。问题由来类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。解决方案尽
- 1、问题引入我们已经完成了后台系统的登录功能开发,但是目前还存在一个问题,就是用户如果不登录,直接访问系统首页面,照样可以正常访问。很明显,
- SpringAOP获取方法参数上的注解一、示例① 如下代码,自定义一个参数注解@Test,并将其使用到方法参数上,用于标注需要检验的参数/*
- 本文实例为大家分享了C#实现简易计算器功能的具体代码,供大家参考,具体内容如下实现页面布局和数值初始化using System;using
- 本文介绍了Android 实现截屏方式整理,分享给大家。希望对大家有帮助可能的需求:截自己的屏截所有的屏带导航栏截屏不带导航栏截屏截屏并编
- springboot整合mybatis项目博客系统文章,相册,评论,标签,等表IDEA为最新版2021.3.3,mysql数据库为最新版Na
- 先来回忆下在mybatis中的resultMap作用和是什么resultMap的作用是什么在使用传统的mybatis时,我们一般都会在xml
- 对于一个简单的tcp通讯这里我就不再讲述了,今天主要为大家讲解下,如何从::recv中筛选出一个完整包逻辑。就简单的以客户端为例(服务器接收
- 一、文件上传的原理分析1、文件上传的必要前提a、表单的method必须是postb、表单的enctype属性必须是multipart/for
- 主要从以下几方面来介绍一下@Scope注解@Scope注解是什么@Scope注解怎么使用@Scope注解的使用场景1,@Scope注解是什么