2015-01-09 43 views
1

以下代碼不會將我的返回值SqlDataReadergetReader正確地轉換爲調用Seq.unfold時的IDataReader我在做什麼錯?此表達式預計具有IDataReader類型,但此處具有SqlDataReader類型

open System.Data 
open System.Data.SqlClient 
open System.Configuration 

type Foo = { id:int; name:string } 

let populateFoo (r:IDataReader) = 
    let o = r.GetOrdinal 
    { id = o "id" |> r.GetInt32; name = o "name" |> r.GetString; } 

let iter populateObject (r:IDataReader) = 
    match r.Read() with 
    | true -> Some(populateObject r, r) 
    | _ -> None 

let iterFoo = iter populateFoo 

let getReader : IDataReader = 
    let cnstr = ConfigurationManager.ConnectionStrings.["db"].ConnectionString 
    let cn = new SqlConnection(cnstr) 
    let cmd = new SqlCommand("select * from Foo", cn) 
    cmd.ExecuteReader() 

let foos = Seq.unfold iterFoo getReader 
+1

我根本不知道F#,但是[MSDN](http://msdn.microsoft.com/en-us/library/dd233220.aspx#code-snippet-4)說'當你上傳時自動應用上傳將參數傳遞給對象類型上的方法。但是,對於模塊中的let-bound函數,上傳不是自動的,除非參數類型被聲明爲靈活類型。所以也許'populateFoo(r:#IDataReader)'會工作嗎? – Rhumborl 2015-01-09 21:00:11

回答

3

F#不自動向上轉型如C#,除了在某些特定情況下(見spec,部分14.4.2)。

您必須明確地投射表達式:cmd.ExecuteReader() :> IDataReader然後您可以在getReader之後刪除類型註釋。

或者你可以保留該函數在調用點返回一個SqlDataReader和向上轉型:

let foos = getReader :> IDataReader |> Seq.unfold iterFoo 

如果unfold是一個類型的,像這樣一個簽名的靜態成員:

type T() = 
    static member unfold(a, b:IDataReader) = Seq.unfold a b 

你將能夠直接做T.unfold(iterFoo, getReader),它會自動上傳。這是規範中提到的其中一例。

+0

在閱讀14.4.2後,我是否正確理解Seq.unfold是問題的來源,並且是因爲它的類型簽名'(('' - >('b *'a)option) - >'a - > seq <'b>)'它不能確定''a'的類型是'IDataReader'還是'SqlDataReader'? – 2015-01-09 21:30:41

+0

@CharlesLambert確切地說,它不符合規範中提到的情況。我在答案中加入了一個假設的例子,它可以爲你提供幫助。 – Gustavo 2015-01-09 21:43:37

+0

你不需要'展開(a,b:#IDataReader)'? – 2015-01-09 21:45:09

相關問題