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

时间:2011-02-24 21:46:19

标签: c# .net arrays linq ienumerable

我有一些像这样的代码:

public class EffectValues : IEnumerable<object>
{
    public object [ ] Values { get; set; }

    public IEnumerator<object> 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实例上使用Linq功能。

2 个答案:

答案 0 :(得分:40)

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

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

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

规则是:

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

请注意,第三点不是

  • 每个数组类型T []实现IList<T>IEnumerable<T>,依此类推,在T []上定义的公共方法和属性隐式实现成员

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

也就是说,想想这样:

namespace System
{
    abstract class Array : IEnumerable
    {
        public IEnumerator GetEnumerator() { ... }
        ...
    }
}

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

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

  

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

魔术!

  

这不是泛型,对吗?

右。数组是非常特殊的类型,它们在深入的CLR类型系统中被烘焙。虽然它们在许多方面与泛型非常相似。

  

似乎这个class object [] : System.Array是用户无法实现的,对吗?

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

  

您认为哪一个更好:将GetEnumerator()投射到IEnumerable<object>,或者只使用foreachyield

问题是形象不对称。您没有将 GetEnumerator 强制转换为IEnumerable<object>。您可以将数组转换为IEnumerable<object>,也可以将 GetEnumerator 转换为IEnumerator<object>

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

  

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

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

  

当你说隐式实现时,你的意思是以Interface.Method的形式,对吗?

不,相反:

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

第一个声明静默实现该方法。第二个是关于它实现的接口方法的显式

  

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

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

  

我原以为Array类型会是这样的:public class Array<T> : IEnumerable<T>等等。但是有一些关于它的神奇代码呢,对吗?

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

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

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

答案 1 :(得分:2)

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

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