zoukankan      html  css  js  c++  java
  • C# 9.0新特性详解系列之二:扩展方法GetEnumerator支持foreach循环

    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
    本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    MySql 用户 及权限操作
    MAC 重置MySQL root 密码
    在mac系统安装Apache Tomcat的详细步骤[转]
    Maven:mirror和repository 区别
    ES6 入门系列
    转场动画CALayer (Transition)
    OC 异常处理
    Foundation 框架
    Enum枚举
    Invalid App Store Icon. The App Store Icon in the asset catalog in 'xxx.app' can’t be transparent nor contain an alpha channel.
  • 原文地址:https://www.cnblogs.com/markkang/p/14018990.html
Copyright © 2011-2022 走看看