认真CS丨yield迭代

首先我们来说下迭代的目的:

迭代是将自定义类变为自定义类数组,并遍历它


在C#1.0和C#2.0这两个版本中,C#2比C#1多了一个更简洁的迭代方法


C#1.0

实现迭代的操作流程:

1、定义单个类—>2、定义这个类的数组的枚举器方法—>3、定义GetEnumerator方法—>4、对该类数组赋值,并实例化3GetEnumerator方法,并将赋值的数组作为实参传入这个方法,进而传到枚举器—>5、调用foreach,对该类数组进行遍历


实例

using System;
using System.Collections;

public class Person
{
    public string firstName, lastName;
    public Person (string fName,string lName)
    {
        firstName = fName;
        lastName = lName;
    }
}

public class PeopleEnum : IEnumerator
{
    public Person[] _people;
    int Position = -1;
    public PeopleEnum(Person[] List)
    {
        _people = List;
    }

    public bool MoveNext()
    {
        Position++;
        return (Position < _people.Length);
    }

    public void Reset()
    {
        Position = -1;
    }

    public object Current
    {
        get
        {
            try
            {
                return _people[Position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new IndexOutOfRangeException();
            }
        }
    }
}

public class People : IEnumerable
{
    private Person[] _people;
    public People(Person[] pArray)
    {
        _people = pArray;
    }

    public IEnumerator GetEnumerator()
    {
        return new PeopleEnum(_people);
    }
}

class Program
{
    static void Main()
    {
        Person[] peopleArray = new Person[3]
        {
            new Person("A","a"),
            new Person("B","b"),
            new Person("C","c")
        };

        People peopleList = new People(peopleArray); 
        foreach(Person p in peopleList)
        {
            Console.WriteLine("firstName:" + p.firstName + "  lastName:" + p.lastName);
        }
    }
}


C#2.0

迭代器

定义:迭代器是C#2.0中的新功能,迭代器块可以是方法主体、访问器主体或运算符主体,他能够使您在类或结构中支持foreach迭代,而不必实现整个IEnumerable接口(不实现它其中的枚举器接口),只需提供一个迭代器,即可遍历类中的数据结构,当编译器检测到迭代器时,他将自动生成IEnumerable接口的Current、MoveNext、Dispose方法。迭代器需引用System.Collections.Generic命名空间


迭代方法1:迭代器创建枚举器 

移除C#1.0的枚举器方法PeopleEnum,在继承了IEnumerable接口的People类中,将GetEnumerator方法中代码改为如下代码,用迭代器创建枚举器

    public IEnumerator GetEnumerator()
    {
       for(int i = 0; i < _people.Length; i++)
        {
            yield return _people[i];
        }
    }

即GetEnumerator方法不再需要枚举器了,而是内部的迭代器yield return语句告诉编译器来自动为我们创建枚举器,yield return语句指定了枚举器中下一个可枚举项

C#1我们要写单个类、IEnumerable接口的实现类(内有GetEnumerator方法)、枚举器类,才能在Main方法实例该类数组遍历它,

C#2我们仅写单个类、IEnumerable接口的实现类,不必写枚举器类,便可在Main方法实例该类数组并遍历它了


案例

using System;
using System.Collections.Generic;

class MyClass
{
    public IEnumerator<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";

    }

    public IEnumerator<string> GetEnumerator()
    {
        return ib();
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (string c in mc)
            Console.WriteLine(c);
    }
}

迭代方法2:迭代器创建可枚举类型

类a包含GetEnumerator方法和一个可枚举类型的迭代器,该可枚举类型迭代器内部又自动实现了GetEnumerator方法和一个可枚举类型

要想遍历该类a,类a的GetEnumerator方法需获取可枚举类型的GetEnumerator方法,从而获得可枚举类型迭代器内部的枚举器

a、迭代器创建class类型

using System;
using System.Collections.Generic;

class MyClass
{
    public class AS
    {
       public int caa;
    }

    public IEnumerable<AS> ib()
    {

        yield return new AS { caa = 10 };
        yield return new AS { caa = 20 };
        yield return new AS { caa = 30 };

    }

    public IEnumerator<AS> GetEnumerator()
    {
        return ib().GetEnumerator();
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (AS c in mc)
            Console.WriteLine(c.caa);
    }
}


b、迭代器创建string类型

using System;
using System.Collections.Generic;

class MyClass
{
    public IEnumerable<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";

    }

    public IEnumerator<string> GetEnumerator()
    {
        return ib().GetEnumerator();
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (string c in mc)
            Console.WriteLine(c);
    }
}


迭代器创建枚举器和创建枚举类型的区别

迭代器创建枚举器:仅生成了一个枚举器,GetEnumerator方法直接获取迭代器(迭代器返回枚举器)

    public IEnumerator<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";

    }

    public IEnumerator<string> GetEnumerator()
    {
        return ib();
    }

迭代器创建可枚举类型:生成了一个枚举器和一个GetEnumerator方法。迭代器外的GetEnumerator方法获取迭代器内的GetEnumerator()方法,从而间接获得迭代器的枚举器,可对该总类进行遍历

 public IEnumerable<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";

    }

    public IEnumerator<string> GetEnumerator()
    {
        return ib().GetEnumerator();
    }


常见迭代器模式

迭代器返回枚举器

当我们实现返回枚举器的迭代器时,必须要实现GetEnumerator来让类可枚举,GetEnumerator方法返回由迭代器返回的枚举器

using System;
using System.Collections.Generic;

class MyClass
{
    public IEnumerator<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";

    }

    public IEnumerator<string> GetEnumerator()
    {
        return ib();
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (string c in mc)
            Console.WriteLine(c);
    }
}


迭代器返回可枚举类型

我们可让总类实现GetEnumerator来让总类本身可被枚举,也可不实现来让类本身不可枚举。但即使不实现GetEnumerator方法,也能实现枚举

a、实现总类的GetEnumerator方法:迭代器方法ib内自动创建了一个枚举器和一个GetEnumerator方法,如果实现总类的GetEnumerator方法,它返回迭代器方法ib返回的GetEnumerator方法(这个方法获取到迭代器的枚举器),从而间接获取到的ib内自动创建的枚举器

using System;
using System.Collections.Generic;

class MyClass
{
    public IEnumerable<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";

    }

    public IEnumerator<string> GetEnumerator()
    {
        return ib().GetEnumerator();
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (string c in mc)
            Console.WriteLine(c);
    }
}


b、不实现GetEnumerator也可枚举的方法:如果不实现总类的GetEnumerator方法,Main方法直接采用IEnumerable类型的迭代器(内有GetEnumerator方法),那么就不需要总类的GetEnumerator方法了

using System;
using System.Collections.Generic;

class MyClass
{
    public IEnumerable<string> ib()
    {

        yield return "caa0";
        yield return "caa1";
        yield return "caa2";
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (string c in mc.ib())
            Console.WriteLine(c);
    }
}


产生多个可枚举类型

在一个类中产生多个可枚举类型,那么就不能能实现GetEnumerator方法,因为可枚举类型的GetEnumerator方法主体有固定格式,无法再次写个GetEnumerator方法,那样就会重名,且Main方法调用的也不知是哪个正确的GetEnumerator方法

一个类产生多个可枚举类型,而是采用不实现GetEnumerator的方法,Main方法直接调用返回可枚举类型的迭代器,即上文的“b、不实现GetEnumerator也可枚举的方法

using System;
using System.Collections.Generic;

class MyClass
{
    string[] colos = { "violet", "blue", "cyan", "green" };

    public IEnumerable<string> Method1()
    {
        for (int i = 0; i < colos.Length; i++)
            yield return colos[i];
    }

    public IEnumerable<string> Method2()
    {
        for (int i = colos.Length-1; i >= 0; i--)
            yield return colos[i];
    }

    static void Main()
    {
        MyClass mc = new MyClass();
        foreach (string c in mc.Method1())
            Console.WriteLine(c);

        foreach (string c in mc.Method2())
            Console.WriteLine(c);
    }
}


将迭代器作为属性

    public IEnumerator<string> shuxing
    {
        get
        {
            for (int i = 0; i < colos.Length; i--)
                yield return colos[i];
        }
    } 

上文讲述的都是将迭代器作为方法。再次重申一下,迭代器块可作为方法主体、访问器块主体和运算符主体


其他注意事项

在编译器生成的IEnumerator枚举器方法中,Reset方法(用来重置)并没有实现,因此调用它会抛出System.NotSpportedception异常。

猜你喜欢

转载自blog.csdn.net/weixin_38239050/article/details/80261388