【问题标题】:Question regarding IEnumerable and IEnumerator关于 IEnumerable 和 IEnumerator 的问题
【发布时间】:2010-12-28 18:38:40
【问题描述】:

我使用以下代码使 myClass 能够使用 foreach。但是我对编程比较陌生,并且在理解以下代码方面有些困难。我在 cmets 中描述了我的问题。如果能提供一些信息,我将不胜感激。

    public class MyClass : IEnumerable<string> 
    {  
    //1) What is IEnumerator for?
        // Whats the difference between IEnumerator and IEnumerable
    public IEnumerator<string> GetEnumerator()     
    {
             yield return "first";         
             yield return "second";     
    }      
    //2) What is it for?  It just calls above method 
   IEnumerator IEnumerable.GetEnumerator()
         {
             return GetEnumerator(); 
         } 
    }
   //3) Lastly what benefits I have from implementing genetic interface 
   //IEnumerable<string> instead of just IEnumerable

【问题讨论】:

  • 我会让 john skeet 回答这个问题
  • @Hasan:我担心他拼错名字会对你做什么。

标签: c# .net ienumerable ienumerator


【解决方案1】:

IEnumerator 和 IEnumerable 有什么区别?

Jason 的回答很好,但我想我只是补充一下我对此的看法。假设你有一个序列:

1, 1, 2, 3, 5, 8, 13, ...

现在假设您有一个箭头指向该序列的某个位置:

1, 1, 2, 3, 5, 8, 13, ...
         ^

“箭头”是一个可以做两件事的对象。首先,它可以给你它所指向的东西。其次,它可以让自己指向下一件事。

IEnumerator 是一个箭头。它有一个属性 Current,它可以为您提供它所指向的东西。它有一个方法,MoveNext(),它使自己指向下一件事。

首先你是如何得到一个箭头的?你需要一个箭厂。你向工厂索要一个箭头,它会给你一个指向序列中第一个元素的箭头。

IEnumerable 是一个箭头工厂。它有一个 GetEnumerator 方法,可以为您提供指向序列第一个元素的箭头。

此方案的一个很好的特性是您可以有多个箭头指向同一序列中的不同位置。

实现通用接口 IEnumerable 而不仅仅是 IEnumerable 有什么好处?

假设序列是整数。如果你实现IEnumerable 那么当你说

foreach(int x in mysequence)

实际要做的是将序列中的 int 转换为对象,将整数装箱,然后立即将对象拆箱回整数,为每个操作添加完全不必要的内存分配。如果编译器知道序列是整数,那么它可以跳过不必要的装箱操作。

假设序列是字符串。如果你实现IEnumerable&lt;string&gt; 那么你可以说:

string first = mysequence.First();

如果你不这样做,那么你不得不说

string first = (string)mysequence.First();

这是不必要且容易出错的。您可以通过使用类型系统简单地保证该类型是字符串,而不是通过强制类型转换来指示编译器。

【讨论】:

  • 带有箭头概念的好比喻。值得一提的是,此模型的一个常见副作用是更改集合开始枚举通常会使指向它的所有“箭头”无效。
  • 关于“GetEnumerator,给你一个指向序列的第一个元素的箭头”:根据MSDN,它给你一个指向一些未定义的箭头事情之前第一件事。
【解决方案2】:

1) IEnumerator 是干什么用的? IEnumeratorIEnumerable 有什么区别?

IEnumerator 是一个接口,表示允许您枚举序列的方法。 IEnumeratorIEnumerable 的区别在于,前者代表了可以枚举序列的对象的约定,而后者代表了可以枚举的序列的对象的约定。

public IEnumerator<string> GetEnumerator() {
         yield return "first";         
         yield return "second";     
}      


IEnumerator IEnumerable.GetEnumerator() {
    return GetEnumerator(); 
} 

2) 它是干什么用的?它只是调用上面的方法。

前者表示合约IEnumerable&lt;string&gt;上方法GetEnumerator的实现。后者表示合约IEnumerable 上方法GetEnumerator 的显式实现。问题是两个合约都有一个名为 GetEnumerator 的方法,但返回类型不同,因此一个方法不能同时满足两个合约(任何实现 IEnumerable&lt;T&gt; 的类也必须将 IEnumerable 实现为 IEnumerable&lt;T&gt; : IEnumerable)。后者调用IEnumerable&lt;string&gt;.GetEnumerator 的实现,因为这是一个合理的实现,它返回IEnumerator 作为IEnumerator&lt;string&gt; : IEnumerator

3) 最后,我从实现通用接口IEnumerable&lt;string&gt; 而不仅仅是IEnumerable 有什么好处?

强大的打字。您知道序列IEnumerable&lt;string&gt; 中的元素都是String 的实例,而对于IEnumerable,您不知道这一点,并且最终可能会尝试将后者的元素转换为String 的实例。不可能。

【讨论】:

    【解决方案3】:

    这是IEnumerable&lt;T&gt;的声明:

    public interface IEnumerable<out T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }
    

    你别无选择,你也实现非泛型 IEnumerable 接口。如果省略 IEnumerable.GetEnumerator() 实现,则会出现编译错误。这是 .NET 1.x 时代遗留下来的包袱,泛型尚不可用,而 .NET 2.0 的过渡期需要支持与 .NET 1.x 程序集的互操作。我们被它困住了。

    【讨论】:

      【解决方案4】:

      1) IEnumerator 是干什么用的?

      IEnumerator 是 MoveNext() 和 Current 成员的真正工作部分。

      IEnumerator 和 IEnumerable 有什么区别

      IEnumerable 是集合的接口,用于表明它具有 GetEnumerator()。

      2) [非泛型 GetEnumerator] 它有什么用?它只是调用上面的方法

      非泛型方法只是为了向后兼容。请注意,通过使用显式实现,它被尽可能地“移出视线”。实施它,因为你必须然后忘记它。

      3) 最后,我从实现遗传接口中获得了什么好处 IEnumerable 而不仅仅是 IEnumerable

      当与foreach 一起使用时,优势很小,因为 foreach 将对循环变量进行类型转换。它会让你在 foreach 中使用var

      foreach (var s in myClassInstance)     // needs `IEnumerable<string>` 
      foreach (string s in myClassInstance)  // works with  `IEnumerable` as well 
      

      但是使用IEnumerable&lt;string&gt;,您还可以拥有到其他领域的类型安全接口,尤其是 LINQ:

      MyClass mc = new MyClass ();
      string s = mc.FirstOrDefault();
      

      【讨论】:

        【解决方案5】:

        GetEnumerator 在泛型被引入语言之前就已经存在,为了不破坏预先存在的代码,默认方法调用泛型实现。

        使用通用枚举,您的类的使用者不需要在迭代时将每个项目转换为字符串。

        【讨论】:

          【解决方案6】:

          关于第三个问题(为什么使用泛型?)这里有一些答案:

          Should we use Generic Collection to improve safety and performance?

          简而言之,尽可能使用泛型集合。

          【讨论】:

            【解决方案7】:

            要了解区别,您需要了解其用途。实际上,它们代表了迭代器设计模式,这是面向对象软件开发人员使用的设计模式之一。

            迭代器模式提供了一种顺序访问集合元素的方法,无需了解集合的结构。

            在传统的设计模式方法中,迭代器模式具有用于创建 Iterator 对象的 Aggregate 接口和用于遍历 Aggregate 对象的 Iterator 接口。

            现在,IEnumerable 充当保证返回迭代器的 Aggregate 接口。迭代器(也称为枚举器)负责生成由特定标准定义的序列中的下一个元素。 IEnumerator 是定义此功能的接口。您可以在我的博文Iterator Pattern C#

            中了解有关此模式的更多信息

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-11-18
              • 1970-01-01
              • 1970-01-01
              • 2012-04-01
              • 1970-01-01
              相关资源
              最近更新 更多