open System
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
type Bar() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () = null
let xs, ys = Foo(), Bar()
for x in xs do () // <--
for y in ys do () // fine
上面的代码产生以下编译错误:
The type 'Foo' is not a type whose values can be enumerated with this syntax, i.e. is not compatible with either seq<_>, IEnumerable<_> or IEnumerable and does not have a GetEnumerator method.
代码看起来完全合法,通用版本工作正常。这是一个F#编译器错误吗?
答案 0 :(得分:5)
我认为这是错误消息和规范之间的不匹配。正如 kvb 指出的那样,规范仅在两种情况下允许for ... in
:
IEnumerable<_>
接口(又名seq<_>
)GetEnumerator
方法时如果类型实现非通用IEnumerable
接口,则它与这两个条件中的任何一个都不匹配。但是,如果将其强制转换为IEnumerable
,那么它实际上将是IEnumerable
类型,它与第二个条件匹配。在类型中直接使用GetEnumerator
成员(如desco建议的那样)也是正确的,因为它也匹配第二种情况。
因此,我认为错误消息不正确,因为它表示实现非泛型IEnumerable
就足够了,但实际上并非如此。
但是,似乎有一个与for
循环相关的实际编译器错误。编写以下代码时会出现编译器“内部错误”(这是不正确的,因为推断的泛型返回类型没有实现IEnumerator
):
type Foo() =
member x.GetEnumerator () = null
for x in Foo() do () // Internal error here
答案 1 :(得分:5)
您的样本可以简化为
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
for x in Foo() do ()
最初,F#编译器试图断言源类型正在实现IEnumerable&lt; _&gt; 在此断言失败后 - 它搜索可访问的GetEnumerator / 0方法,该方法返回具有可访问的MoveNext()/ Current成员的类型。似乎来自IEnumerable的显式实现的方法在Foo类型中是不可见的,因为下面的代码是有效的:
open System
open System.Collections
type Foo() =
member x.GetEnumerator () : IEnumerator = null
for x in Foo() do () // GetEnumerator is accessible in Foo
或
open System
open System.Collections
type Foo() =
interface IEnumerable with
member x.GetEnumerator () : IEnumerator = null
for x in (Foo() :> IEnumerable) do () // IEnumerable has accessible GetEnumerator
答案 2 :(得分:3)
我不这么认为,但这不是一个非常有用的错误信息。有关如何评估for ... in ... do ...
表达式的详细信息,请参阅规范的Sequence Iteration Expressions部分。如果类型实现IEnumerable<_>
,则模式正如预期的那样工作。否则,编译器会使用正确的签名查找公共(规范说“可访问”)GetEnumerator
方法并调用它。由于F#接口实现是显式的,因此如果不将GetEnumerator
类型向上转换为Foo
,则无法使用IEnumerable
方法。如果您执行upcast,您的代码将再次按预期工作:
for x in (xs :> Collections.IEnumerable) do () // fine