为什么在F#中,我可以这样做......
let s = seq { for i in 0 .. 4095 do yield i } :?> IEnumerator
...但这会引发System.InvalidCastException
?
let s = Seq.init 4095 (fun i -> i) :?> IEnumerator
答案 0 :(得分:6)
序列表达式创建一个实现IEnumerable<T>
和IEnumerator<T>
let s = seq { for i in 0 .. 4095 do yield i }
printfn "%b" (s :? IEnumerable<int>) // true
printfn "%b" (s :? IEnumerator<int>) // true
但Seq.init
没有:
let s = Seq.init 4095 (fun i -> i)
printfn "%b" (s :? IEnumerable<int>) // true
printfn "%b" (s :? IEnumerator<int>) // false
您可以重构代码以使用IEnumerable<T>
代替IEnumerator
,因为这两个构造都会生成IEnumerable<T>
。
或者,如果您真的需要IEnumerator
,则只需致电GetEnumerator
即可从Enumerator
返回Enumerable
:
let s = (Seq.init 4095 (fun i -> i)).GetEnumerator()
printfn "%b" (s :? IEnumerable<int>) // false
printfn "%b" (s :? IEnumerator<int>) // true
答案 1 :(得分:5)
如果查看the specification,序列表达式将转换为:
Seq.collect (fun pat -> Seq.singleton(pat)) (0 .. 4095)
如果你查看Seq.collect
定义的来源,那就是:
let collect f sources = map f sources |> concat
如果你看一下concat
的定义,那就是:
let concat sources =
checkNonNull "sources" sources
mkConcatSeq sources
mkConcatSeq
定义为:
let mkConcatSeq (sources: seq<'U :> seq<'T>>) =
mkSeq (fun () -> new ConcatEnumerator<_,_>(sources) :> IEnumerator<'T>)
因此您可以看到返回的序列实现了IEnumerator<'T>
,因此IEnumerator
。
现在Seq.init
定义为:
let init count f =
if count < 0 then invalidArg "count" (SR.GetString(SR.inputMustBeNonNegative))
mkSeq (fun () -> IEnumerator.upto (Some (count-1)) f)
和mkSeq
定义为:
let mkSeq f =
{ new IEnumerable<'U> with
member x.GetEnumerator() = f()
interface IEnumerable with
member x.GetEnumerator() = (f() :> IEnumerator) }
所以它只实现IEnumerable<'T>
而不是IEnumerator
。