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# 编译器错误吗?
我认为这是错误消息与规范之间的不匹配。正如 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
您的样本可以简化为
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
for x in Foo() do ()
最初,F# 编译器尝试断言源类型正在实现 IEnumerable <_> 此断言失败后 - 它会搜索可访问的 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
我不这么认为,但这不是一个非常有用的错误消息。 有关如何计算 for ... in ... do ...
表达式的详细信息,请参阅规范的
Sequence Iteration Expressions部分。 如果该类型实现了
IEnumerable<_>
,则该模式将按预期工作。 否则,编译器会查找具有正确签名的公共(规范说“可访问”)GetEnumerator
方法并调用它。 由于 F# 接口实现是显式的,因此如果不将 GetEnumerator
类型向上转换为 Foo
,则 IEnumerable
方法不可用。 如果您执行向上转换,您的代码将再次按预期工作:
for x in (xs :> Collections.IEnumerable) do () // fine