什么时候应该创建派生自 IEnumerator/IEnumerable 的 类?

When should you create classes that are derived from IEnumerator/IEnumerable?

我确实了解这两个接口(IEnumeratorIEnumerable)的工作原理以及它们的用途。但是,我一直不太明白何时创建从这两个接口之一派生的 class 。据我所知,您可以在列表、数组和其他通用集合上执行 foreach 循环,而不必像我在代码中所做的那样创建 class:

 class Program
{
   public static int[] array = new int[3] { 1, 2, 3 };
    static void Main(string[] args)
    {
        var Enumerable = new Aninfiniteenumerator();
        foreach(var i in infiniteEnumerable)
        {
            Console.WriteLine($"I is {i}");
        }
        Console.ReadKey();
    }
}
class Aninfiniteenumerator : IEnumerable<int> //Creating a class that is derived from IEnumerable
{  
    public IEnumerator GetEnumerator()
    {
        return new MyInfiniteEnumer(Program.array);
    }

    IEnumerator<int> IEnumerable<int>.GetEnumerator()
    {
        return new MyInfiniteEnumer(Program.array);
    }
}
public class MyInfiniteEnumer : IEnumerator<int> ////Creating a class that is derived from IEnumerator
{
    private int[] values;
    private int index = -1;
    public int Current => values[index];

    object IEnumerator.Current => Current;
    public MyInfiniteEnumer (int [] values)
    {
        this.values = values;
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        index++;
        return index < values.Length;
    }

    public void Reset()
    {
        
    }
}

PS。我知道我的枚举数被称为“无限枚举数”,但它们并不是无限的。因此,正如我所说,可以在通用 list/an 数组上执行 foreach 循环,而不必创建 IEnumerable/IEnumerator classes :

 class Program
{
    static void Main(string[] args)
    {
        Random rand = new Random();
        List<Car> vehicles = new List<Car>();
        for(int i = 0; i < 100; i++)
        {
            vehicles.Add(new Car(rand.Next(1970,2021), "Honda"));
        }
        foreach(var car in vehicles)
        {
            Console.WriteLine(car.yearProduced + ", " + car.model);
        }
        Console.ReadKey();
        for (int i = 0; i < 100; i++)
        {
            vehicles.Add(new Car(rand.Next(1970, 2021), "Subaru"));
        }
        foreach (var car in vehicles)
        {
            Console.WriteLine(car.yearProduced + ", " + car.model);
        }
        Console.WriteLine(vehicles.Count());
        Console.ReadKey();
    }
}
class Car
{
    public string model { get; set; }
    public int yearProduced { get; set; }
    public Car (int year, string model)
    {
        yearProduced = year;
        this.model = model;
    }
}

回到我的问题,我编写了第一个代码,以便能够在名为“Enumerable”的 var 上执行 foreach 循环。我只是为了练习而编写这段代码,并没有看到创建从 IEnumerable 和 IEnumerator 派生的 classes 的实际用途。所以,我的问题是,在哪些情况下您必须创建从这两个接口之一派生的 class?

可以使用枚举器的自定义结构的一个很好的例子是 二叉树

最简单的树是

public class Tree
{
   public Tree Left { get; set; }
   public Tree Right { get; set; }
   public object Value { get; set; }
}

这个定义可以用来组合任意树:

var root = new Tree()
{
   Left = new Tree() { Value = 1 },
   Right = new Tree() { Value = 2 },
   Value = 3
}

请注意,我的树是平衡的,但实际的树不一定是。

现在,您应该如何枚举所有值?这不是那么容易,在每个节点中你必须决定它是否有子节点,当你在节点上完成后,你必须回到它的父节点并探索所有路径。

但是客户端代码对难不难不感兴趣,它期望代码是

 foreach ( var val in root )
 {
    // I want all values from the tree here!
 }

此时枚举的概念开始变得有意义了。它不仅仅是关于简单的线性结构,如数组或列表!

 public class Tree : IEnumerable
 {
     public IEnumerator GetEnumerator()
     {
         if ( Left != null ) foreach ( var e in Left ) yield return e;
         if ( Right != null ) foreach ( var e in Right ) yield return e;
         yield return Value;
     }
 }

我希望这个例子能对这个问题有所启发。