为什么我不能使用数组的枚举器,而不是自己实现它?

我有一些像这样的代码:

public class EffectValues : IEnumerable { public object [ ] Values { get; set; } public IEnumerator GetEnumerator ( ) { return this.Values.GetEnumerator ( ); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ( ) { return this.GetEnumerator ( ); } } 

但编译器抱怨说:

“无法将类型’System.Collections.IEnumerator’隐式转换为’System.Collections.Generic.IEnumerator’。存在显式转换(您是否错过了转换?)”

我以为Array类型实现了IEnumerable接口,不是吗? 因为我可以直接在Values实例上使用Linqfunction。

这是一个微妙的,有点不幸。 简单的解决方法是:

 public IEnumerator GetEnumerator ( ) { return ((IEnumerable)this.Values).GetEnumerator ( ); } 

我以为Array类型实现了IEnumerable接口,不是吗?

规则是:

  • System.Array使用公共方法“隐式”实现IEnumerable。
  • 每个数组类型T []都inheritance自System.Array。
  • 每个数组类型T []实现IListIEnumerable等。
  • 因此每个数组类型T []都可以转换为IEnumerable

请注意,第三点不是

  • 每个数组类型T []实现IListIEnumerable等等, 在T []上定义的公共方法和属性隐式实现成员

你去吧 当你查找GetEnumerator时,我们在object []上查找并找不到它,因为object [] 显式地实现了IEnumerable 。 它可以转换IEnumerable ,并且convertiable不计入查找。 (你不会期望在int上出现“double”方法只是因为int可以转换为double。)然后我们查看基类型,发现System.Array使用公共方法实现IEnumerable,所以我们已经找到了我们的GetEnumerator。

也就是说,想想这样:

 namespace System { abstract class Array : IEnumerable { public IEnumerator GetEnumerator() { ... } ... } } class object[] : System.Array, IList, IEnumerable { IEnumerator IEnumerable.GetEnumerator() { ... } int IList.Count { get { ... } } ... } 

当你在object []上调用GetEnumerator时,我们看不到实现是一个显式的接口实现,所以我们转到基类,它有一个可见的。

如何“动态”生成所有object [],int [],string [],SomeType []类?

魔法!

这不是generics,对吧?

对。 数组是非常特殊的类型,它们可以深入到CLR类型系统中。 虽然它们在许多方面与generics非常相似。

看起来这个class object [] : System.Array是用户无法实现的东西,对吧?

对,这只是为了说明如何思考它。

您认为哪一个更好:将GetEnumerator()IEnumerable ,或者只使用foreachyield

问题是形象不对称。 您不会将GetEnumerator强制转换IEnumerable 。 您可以将数组转换IEnumerable也可以将GetEnumerator转换IEnumerator

我可能会将值转换为IEnumerable并在其上调用GetEnumerator

我可能会使用转换,但我想知道这是否是你或某些程序员可以阅读代码的地方,会认为它不太清楚。

我认为演员阵容非常清楚。

当你说隐式实现时,你的意思是Interface.Method的forms,对吧?

不,相反:

 interface IFoo { void One(); void Two(); } class C : IFoo { public void One() {} // implicitly implements IFoo.One void IFoo.Two() {} // explicitly implements IFoo.Two } 

第一个声明默默地实现该方法。 第二个是明确它实现的接口方法。

实现IEnumerable的原因是什么,而不是公共方法的隐式实现? 我很好奇,因为你说“这是一个微妙而有点不幸”,所以看起来这是因为一个较老的决定迫使你做我想象的事情?

我不知道是谁做出了这个决定。 不过有点不幸。 它至少让一个用户感到困惑 – 你 – 它也让我困惑了几分钟!

我原本认为Array类型是这样的: public class Array : IEnumerable等。但是有一些神奇的代码呢,对吧?

对。 正如你在昨天的问题中所指出的那样,如果我们在CLR v1中使用generics,事情会有很大的不同。

数组本质上是一种通用的集合类型。 因为它们是在没有generics的类型系统中创建的,所以类型系统中必须有许多特殊代码来处理它们。

下次设计一个类型系统时,将generics置于v1中并确保获得强大的集合类型,从一开始就将可空类型和非可空类型烘焙到框架中。 事后添加generics和可空值类型很困难。

您必须将数组转换为IEnumerable才能访问通用枚举器:

 public IEnumerator GetEnumerator() { return ((IEnumerable)this.Values).GetEnumerator(); }