2014-04-17 17 views
4

爲什麼在F#中,我能做到這一點...F#:低迷現狀,以次來的IEnumerator

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

...但是這將引發System.InvalidCastException

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

你確定你不想IEnumerable而不是IEnumerator? – ildjarn

+0

我正在爲Unity編寫協程。必須是IEnumerator。 – MiloDC

回答

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返回一個EnumeratorEnumerable

let s = (Seq.init 4095 (fun i -> i)).GetEnumerator() 
printfn "%b" (s :? IEnumerable<int>) // false 
printfn "%b" (s :? IEnumerator<int>) // true 
+0

當,偷走了我的答案。好吧,其中的一部分......不知道序列表達式與Seq模塊的對應表達式並不完全相同。 – Jwosty

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

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

因爲現在Seq.init定義。