2014-04-17 5 views
4

Почему в F #, я могу это сделать ...F #: Downcast след в 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

Я пишу сопрограмму для Единства. Должен быть 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, чтобы возвращать Enumerator из Enumerable:

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.

Теперь 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.

 Смежные вопросы

  • Нет связанных вопросов^_^