关键词搜索

源码搜索 ×
×

C# 9.0新特性——扩展方法GetEnumerator支持foreach循环

发布2021-05-11浏览404次

详情内容

1.介绍

我们知道,我们要使一个类型支持foreach循环,就需要这个类型满足下面条件之一:

该类型实例如果实现了下列接口中的其中之一:

System.Collections.IEnumerable
System.Collections.Generic.IEnumerable
System.Collections.Generic.IAsyncEnumerable
该类型中有公开的无参GetEnumerator()方法,且其返回值类型必须是类,结构或者接口,同时返回值类型具有公共 Current 属性和公共无参数且返回类型为 Boolean的MoveNext 方法。

上面的第一个条件,归根结底还是第二个条件的要求,因为这几个接口,里面要求实现的还是GetEnumerator方法,同时,接口中GetEnumerator的返回值类型IEnumerator接口中要实现的成员和第二条中返回值类型的成员相同。

C#9.0之前,是不支持采取扩展方法的方式给类型注入GetEnumerator方法,以支持foreach循环的。从C#9.0之后,这种情况得到了支持。

  1. 应用与示例

在这里,我们定义一个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();
      }
    }
    
      2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    需要注意的是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;
    }
    
      2
    • 3
    • 4
    • 5

    最后,只要引用了扩展方法所在的命名空间,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);
    }
    
      2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    到这里,我们就完成了利用扩展方法来实现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;
    }
    
      2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    结束语

    解除原有的限制,扩展方法GetEnumerator支持foreach循环,为特殊的需要提供了一种可能。

    作者:MarkKang
    出处:https://www.cnblogs.com/markkang/

    以上就是C# 9.0新特性——扩展方法GetEnumerator支持foreach循环的详细内容

    相关技术文章

    点击QQ咨询
    开通会员
    返回顶部
    ×
    微信扫码支付
    微信扫码支付
    确定支付下载
    请使用微信描二维码支付
    ×

    提示信息

    ×

    选择支付方式

    • 微信支付
    • 支付宝付款
    确定支付下载