使用 IEnumerator 和使用数组作为 属性 的区别

Difference between using IEnumerator and using an array as an property

我得到了 IEnumerator 的想法,它为 class 提供了迭代能力。我明白为什么需要 GetEnumerable() 才能使用 foreach 语句。但是我想知道为什么我需要实现 IEnumerator 而我可以将数组声明为 属性?我真的很困惑,可以使用一个很好的解释。这是我的代码块,使用和不使用 IEnumerator 实现的结果是一样的。

static void Main(string[] args)
    {

        Customer customer1 = new Customer
        {
            Name = "Marty",
            Surname = "Bird",
            Id = 1
        };
        Customer customer2 = new Customer
        {
            Name = "Hellen",
            Surname = "Pierce",
            Id = 2
        };
        Customers customers = new Customers();
        customers.Add(customer1);
        customers.Add(customer2);
        //Using a property: This one works without an extra GetEnumerator implementation.
        foreach (var item in customers.CustomerList)
        {
            Console.WriteLine(item.Name);
        }
        //Using with GetEnumerator()
        foreach (Customer item in customers)
        {
            Console.WriteLine(item.Name);
        }

    }
    class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
    }

    class Customers:IEnumerable
    {
        private List<Customer> customerList = new List<Customer>();

        public List<Customer> CustomerList { get=> customerList; }
        public void Add(Customer p)
        {
            customerList.Add(p);
        }

        public IEnumerator GetEnumerator()
        {
            return customerList.GetEnumerator();
        }
    }

因为数组实现了GetEnumerator()。您的 class 可能并不总是将可枚举的内容存储在已经为您实现的数据结构中。由于这种模式非常灵活,您可以变得非常有创意。

以这个class为例:

class Numbers1To5 : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator() => new Numbers1To5Enumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    class Numbers1To5Enumerator : IEnumerator<int>
    {
        public int Current { get; private set; }
        object IEnumerator.Current => Current;

        public bool MoveNext()
        {
            if (Current >= 5)
                return false;

            Current++;

            return true;
        }

        public void Reset()
        {
            Current = 0;
        }

        public void Dispose()
        {
        }
    }
}

如果您有执行以下操作的程序,您会在控制台中显示数字 1 到 5:

foreach (int number in new Numbers1To5())
    Console.WriteLine(number);

数组属性经常会导致错误。我们开发了一个可靠的数组 属性 来避免 foreach 循环中的错误。 Microsoft 建议一种不使用 null 值的设计模式,即使在没有数据时也是如此。

class Program
{
    static void Main()
    {
        foreach (string item in Empty)
        {
            System.Console.WriteLine(item); // Never reached
        }
    }

    /// Get an empty array.
    public static string[] Empty
    {
        get
        {
            return new string[0]; // Won't crash in foreach
        }
    }
}

NET 的 IEnumerable 接口命名错误 - 它应该是 IIterable。基本上 System.Collection.IEnumerable 或(因为泛型)System.Collection.Generic.IEnumerable 允许您在实现这些 interfaces.IEnumerable 的对象上使用 foreach,另一方面,基本上说给定一个起始位置是可能的下一个值。这方面的一个例子可能是无限系列的数字:

public IEnumerable<int> Infinite()
{
    int i = 0;
    while(true)
        yield return i++;
}

您真的根本不需要实现 IEnumerator。您可以去掉 Customers class 并替换行:

Customers customers = new Customers();

有了这个:

List<Customer> customers = new List<Customers>();

您需要实现 GetEnumerator 的唯一原因是因为您从 IEnumerable 继承了 class Customers,这是一个声明该方法的接口。接口中的所有方法和属性都必须在实现该接口的 class 中实现。

根据您展示的代码,您不需要客户 class,只需使用我上面解释的列表。

但是,如果您确实需要 Customers class,因为您计划向其添加比您展示的更多的功能,一种选择是从 List<Customer> 继承。然后它将在 foreach 语句中工作而无需实现 GetEnumerable() 因为 List 已经实现了该方法。

如果你想推出自己的可枚举 Customers class,非常简单:

class Customers : List<Customer>
{
}

就是这样!

现在您可以这样做了:

Customer customer1 = new Customer
{
    Name = "Marty",
    Surname = "Bird",
    Id = 1
};
Customer customer2 = new Customer
{
    Name = "Hellen",
    Surname = "Pierce",
    Id = 2
};
Customers customers = new Customers();
customers.Add(customer1);
customers.Add(customer2);
foreach (var item in customers)
{
    Console.WriteLine(item.Name);
}

话虽如此,您可能想阅读此答案:Is it a good practice to create your own collection class?。在大多数情况下,您可以跳过特殊的 class 并直接使用 List<Customer>