7

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

    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
4

7 回答 7

12

IEnumerator 和 IEnumerable 有什么区别?

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

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<string>,那么你可以说:

string first = mysequence.First();

如果你不这样做,那么你必须说

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

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

于 2010-12-28T22:57:07.773 回答
6

1) 是IEnumerator为了什么?IEnumerator和有什么区别IEnumerable

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

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


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

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

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

3)最后我从实现通用接口IEnumerable<string>而不是仅仅得到什么好处IEnumerable

强打字。您知道序列中的元素IEnumerable<string>都是 的实例,String而您不知道这一点,IEnumerable并且最终可能会尝试将后者的元素强制转换为String无法实现的实例。

于 2010-12-28T18:54:39.450 回答
3

1) IEnumerator 是做什么用的?

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

IEnumerator 和 IEnumerable 有什么区别

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

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

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

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

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

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

但是 IEnumerable<string>你也有一个到其他领域的类型安全接口,最显着的是 LINQ:

MyClass mc = new MyClass ();
string s = mc.FirstOrDefault();
于 2010-12-28T18:54:51.733 回答
3

这是声明IEnumerable<T>

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

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

于 2010-12-28T19:00:29.247 回答
0

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

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

于 2010-12-28T18:41:57.327 回答
0

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

我们应该使用通用集合来提高安全性和性能吗?

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

于 2010-12-28T18:44:58.007 回答
0

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

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

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

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

于 2019-10-25T18:25:14.477 回答