我有一些像这样的代码:
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功能。
答案 0 :(得分:40)
这是一个微妙而有点不幸的事。简单的解决方法是:
public IEnumerator<object> GetEnumerator ( )
{
return ((IEnumerable<object>)this.Values).GetEnumerator ( );
}
我认为Array类型实现了两个IEnumerable接口,不是吗?
规则是:
IList<T>
,IEnumerable<T>
等等。IEnumerable<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>
,或者只使用foreach
和yield
?
问题是形象不对称。您没有将 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();
}