我使用以下代码使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
答案 0 :(得分:11)
杰森的回答很好,但我想我只想补充一下我对此的看法。想象一下你有一个序列: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转换为object,装箱整数,然后立即将对象取消装回整数,为每个操作添加一个完全不必要的内存分配。如果编译器知道序列是整数,那么它可以跳过不必要的装箱操作。
假设序列是字符串。如果您实施IEnumerable<string>
,那么您可以说:
string first = mysequence.First();
如果你不这样做,那么你必须说
string first = (string)mysequence.First();
这是不必要的,容易出错。而不是通过类型为字符串的强制转换来指示编译器,您可以通过使用类型系统保证该类型是字符串。
答案 1 :(得分: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>
的类都必须实现IEnumerable
作为{ {1}})。后者调用IEnumerable<T> : IEnumerable
的实现,因为这是一个合理的实现,它将IEnumerable<string>.GetEnumerator
作为IEnumerator
返回。
3)最后,我实现通用接口
IEnumerator<string> : IEnumerator
而不仅仅是IEnumerable<string>
有什么好处?
强力打字。您知道序列IEnumerable
中的元素都是IEnumerable<string>
的实例,而您不知道String
的元素,并且最终可能会尝试将后者的元素强制转换为实例IEnumerable
当它不可能时。
答案 2 :(得分:3)
1)什么是IEnumerator?
IEnumerator是MoveNext()和Current成员的真正工作部分。
IEnumerator与IEnumerable之间的区别是什么
IEnumerable是集合的接口,表示它具有GetEnumerator()。
2)[非泛型GetEnumerator]它的用途是什么?它只是调用上面的方法
非泛型方法只是为了向后兼容。请注意,通过使用显式实现,它尽可能地“移出视线”。实施它是因为你必须忘记它。
3)最后我实施遗传接口有什么好处 IEnumerable而不仅仅是IEnumerable
当与foreach
一起使用时,adavantage很小,因为foreach会输出循环变量。它会让你在foreach中使用var
:
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();
答案 3 :(得分:3)
这是IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
您别无选择, 也可以实现非通用的IEnumerable接口。如果省略IEnumerable.GetEnumerator()实现,则会出现编译错误。这是.NET 1.x期间遗留下来的行李,其中泛型不可用,以及.NET 2.0需要支持与.NET 1.x程序集互操作的过渡期。我们一直坚持下去。
答案 4 :(得分:0)
GetEnumerator
一直存在于将泛型引入语言之前,为了不破坏预先存在的代码,默认方法调用通用实现。
使用泛型枚举,您的类的使用者在迭代时不需要将每个项目转换为字符串。
答案 5 :(得分:0)
关于第3个问题(为什么要使用泛型?),这里有一些答案:
Should we use Generic Collection to improve safety and performance?
简而言之,每次都可以使用通用集合。
答案 6 :(得分:0)
要了解差异,您需要了解其用法。实际上,它们表示Iterator设计模式,它是面向对象的软件开发人员使用的设计模式之一。
Iterator模式提供了一种顺序访问集合元素的方式,而无需知道集合的结构。
在传统的设计模式方法中,迭代器模式具有用于创建Iterator对象的Aggregate接口和用于遍历Aggregate对象的Iterator接口。
现在,IEnumerable充当保证返回迭代器的Aggregate接口。迭代器(也称为枚举器)负责按某些条件定义的顺序生成下一个元素。 IEnumerator是定义此功能的接口。您可以在我的博客文章Iterator Pattern C#
中了解有关此模式的更多信息