【问题标题】:Difference between using IEnumerator and using an array as an property使用 IEnumerator 和使用数组作为属性之间的区别
【发布时间】:2020-09-01 21:10:28
【问题描述】:

我得到了 IEnumerator 的想法,它赋予了类迭代的能力。我明白为什么需要 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();
        }
    }

【问题讨论】:

  • 谁说需要实现IEnumerator
  • foreach 的定义是这么说的。如果你想要一个可迭代的类,你需要它继承自 IEnumerator 接口或继承自 IEnumerator 的类。
  • 所以如果你想要可迭代的类 - 你实现它。通常你不会。如果您出于某种原因实现新的集合/可迭代类型,您将需要。同样,通常你不会,你只是重复使用现有的。
  • @muaet 实际上,foreach 只需要你传递给它的东西就有一个 GenEnumerator 方法,该方法返回带有 MoveNext 方法和 Current 属性的东西。
  • CustomerList 属性使您的类的用户可以原始访问您的私有字段,这不是很好的数据封装。如果您删除该字段,那么您就可以通过IEnumerable 界面进行访问。

标签: c# arrays .net implementation ienumerator


【解决方案1】:

您根本不需要实现 IEnumerator。您可以摆脱客户类并替换该行:

Customers customers = new Customers();

用这个:

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

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

刚刚给出的代码你不需要客户类,只需使用我上面解释的列表。

但是,如果您确实需要一个客户类,因为您计划向它添加比您所展示的更多的功能,那么一种选择是从List&lt;Customer&gt; 继承。然后它可以在 foreach 语句中工作,而无需实现 GetEnumerable(),因为 List 已经实现了该方法。

【讨论】:

  • 我明白了。从已经实现 IEnumerator 的类继承更容易。假设我需要更多功能并且需要使用客户类,使用数组作为属性是否存在问题,因为它已经是可迭代的?
  • 不,使用数组作为属性是没有问题的,要么通过方法暴露IEnumerator,要么将数组暴露为公共属性等,但确实是偏好在那时候。这取决于你想如何设计你的类,以及如何使用它等。如果不知道意图,就没有明确的答案。 John Wu 的回答中提供的链接提供了一些很好的指导。
【解决方案2】:

数组属性经常会导致错误。我们开发了一个可靠的数组属性,可以避免 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 允许您在实现这些接口的对象上使用 foreach。另一方面,IEnumerable 基本上表示给定一个起始位置,可以得到下一个值。这方面的一个例子可能是一系列无限的数字:

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

【讨论】:

  • 谢谢。如果我们使用列表作为属性可能会出现什么样的问题?
【解决方案3】:

如果你想推出自己的可枚举Customers 类,这很容易:

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?。在大多数情况下,您可以跳过特殊类,直接使用List&lt;Customer&gt;

【讨论】:

  • 非常感谢。我明白了。确实很实用。但是你能告诉我有什么充分的理由不使用数组作为属性来获得可迭代性吗?可能是优化、性能或安全性。每个人都提出了一些建议,其中一些非常有用,就像你的一样,但这并不能回答困扰我的问题^^你能提供见解吗?
【解决方案4】:

这是因为数组实现了GetEnumerator()。您的类可能并不总是将可枚举的东西存储在已经为您实现的数据结构中。由于这种模式非常灵活,因此您可以发挥很大的创造力。

以这个类为例:

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);

【讨论】:

  • 所以你的意思是如果我们需要比列表或数组更复杂的迭代规则,我们可以使用实现来定制。正确的?我想知道我们可以声明与数组不同的哪种迭代方法?
猜你喜欢
  • 2023-03-17
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 2010-10-31
  • 2011-10-08
  • 2012-10-03
  • 2011-06-22
  • 2012-08-27
相关资源
最近更新 更多