F#:Downcast seq to IEnumerator

时间:2014-04-17 21:03:27

标签: f# ienumerator seq

为什么在F#中,我可以这样做......

let s = seq { for i in 0 .. 4095 do yield i } :?> IEnumerator

...但这会引发System.InvalidCastException

let s = Seq.init 4095 (fun i -> i) :?> IEnumerator

2 个答案:

答案 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