2010-06-07 47 views
11

我想遞歸地打印出所有的對象的屬性和子類型的屬性等等。我的對象模型如下匹配...F#模式與類型

type suggestedFooWidget = { 
    value: float ; 
    hasIncreasedSinceLastPeriod: bool ; 
} 

type firmIdentifier = { 
    firmId: int ; 
    firmName: string ; 
} 
type authorIdentifier = { 
    authorId: int ; 
    authorName: string ; 
    firm: firmIdentifier ; 
} 

type denormalizedSuggestedFooWidgets = { 
    id: int ; 
    ticker: string ; 
    direction: string ; 
    author: authorIdentifier ; 
    totalAbsoluteWidget: suggestedFooWidget ; 
    totalSectorWidget: suggestedFooWidget ; 
    totalExchangeWidget: suggestedFooWidget ; 
    todaysAbsoluteWidget: suggestedFooWidget ; 
    msdAbsoluteWidget: suggestedFooWidget ; 
    msdSectorWidget: suggestedFooWidget ; 
    msdExchangeWidget: suggestedFooWidget ; 
} 

我的遞歸是基於下面的模式匹配...

let rec printObj (o : obj) (sb : StringBuilder) (depth : int) 
    let props = o.GetType().GetProperties() 
    let enumer = props.GetEnumerator() 
    while enumer.MoveNext() do 
     let currObj = (enumer.Current : obj) 
     ignore <| 
      match currObj with 
      | :? string as s -> sb.Append(s.ToString()) 
      | :? bool as c -> sb.Append(c.ToString()) 
      | :? int as i -> sb.Append(i.ToString()) 
      | :? float as i -> sb.Append(i.ToString()) 
      | _ -> printObj currObj sb (depth + 1) 
    sb 

在調試器中我看到currObj是字符串類型,整數,浮點,等等,但它總是跳轉到在底部的defualt情況。任何想法爲什麼發生這種情況?

回答

4

下面是我得到它的工作...

let getMethod = prop.GetGetMethod() 
let value = getMethod.Invoke(o, Array.empty) 
    ignore <| 
     match value with 
     | :? float as f -> sb.Append(f.ToString() + ", ") |> ignore 
          ... 
0

你想要這樣的東西,而不是。

let rec printObj (o : obj) (sb : StringBuilder) (depth : int) 
    let props = o.GetType().GetProperties() :> IEnumerable<PropertyInfo> 
    let enumer = props.GetEnumerator() 
    while enumer.MoveNext() do 
     let currObj = (enumer.Current.GetValue (o, null)) :> obj 
     ignore <| 
      match currObj with 
      | :? string as s -> sb.Append(s.ToString()) 
      | :? bool as c -> sb.Append(c.ToString()) 
      | :? int as i -> sb.Append(i.ToString()) 
      | :? float as i -> sb.Append(i.ToString()) 
      | _ -> printObj currObj sb (depth + 1) 
    sb 

這是從MSDN文檔來對Array類:

在.NET Framework 2.0版中, 陣列類實現 System.Collections.Generic.IList, 系統.Collections.Generic.ICollection, 和 System.Collections.Generic.IEnumerable 通用接口。 實現在運行時提供給陣列 ,因此不是 工具對文檔構建 可見。其結果是,通用 接口不出現在陣列 類 聲明語法,並沒有參考 主題爲接口的成員,只有鑄造數組 通用接口類型是 訪問(明確 接口實現)。關鍵 當您將 陣列轉換爲其中一個接口時,要注意的事項是 ,即添加,插入或刪除元素的成員 NotSupportedException。

+0

編譯器告訴我: 領域,構造或成員「的GetValue」沒有定義 – PhilBrown 2010-06-07 18:48:49

+0

@philbrowndotcom - 我認爲你需要做一個演員。 – ChaosPandion 2010-06-07 20:04:49

0

確定該程序的行爲與預期不符?調試器跨度並不總是可靠的。

+0

問題是enumer.Current.GetValue並不是真正的值,它是一個PropertyInfo對象。這裏有一些抽象讓我感到困惑。 – PhilBrown 2010-06-07 20:01:01

+0

那麼你使用GetType/GetProperties/etc的API是什麼?也許文檔解釋了這一點? – Brian 2010-06-07 20:27:41

1

在你的例子中,enumer.Current是一個包含PropertyInfo的對象。這意味着currObj始終是一個PropertyInfo對象,並且將始終對應於您的匹配語句中的最後一種情況。

既然你感興趣的財產價值的類型,你就需要調用的PropertyInfo的的GetValue()方法來獲得該財產的實際價值(如ChaosPandion的答案) 。

由於Enumerator將其值作爲對象返回,因此在訪問GetValue之前,還需要將enum.current強制轉換爲PropertyInfo。

嘗試

let currObj = unbox<PropertyInfo>(enumer.Current).GetValue (o, null) 

隨着這一變化更換

let currObj = (enumer.Current : obj) 

,我可以讓你的代碼工作(在FSI):

> let test = {authorId = 42; authorName = "Adams"; firm = {firmId = 1; firmName = "GloboCorp inc."} };; 
> string <| printObj test (new StringBuilder()) 1;; 
val it : string = "42Adams1GloboCorp inc." 
14

正如其他人所指出的,您需要調用GetValue成員來獲取該屬性的值 - 您實現的迭代iterat而不是PropertyInfo對象,它們是「屬性的描述符」 - 不是實際值。但是,我不太明白,爲什麼當明確使用GetEnumeratorwhile循環時,可以使用for循環來編寫相同的內容。

而且,你不需要忽略由sb.Append調用的返回值 - 你可以簡單地返回它的總的結果(因爲它是StringBuilder)。這實際上會使代碼更有效率(因爲它支持尾部調用優化)。作爲最後一點,sb.Append(..)中不需要ToString,因爲Append方法已過載並適用於所有標準類型。

所以一些簡化後,就可以得到這樣的事情(這不是真正使用depth參數,但我猜你想以後用它的東西):

let rec printObj (o : obj) (sb : StringBuilder) (depth : int) = 
    let props = o.GetType().GetProperties() 
    for propInfo in props do 
    let propValue = propInfo.GetValue(o, null) 
    match propValue with 
    | :? string as s -> sb.Append(s) 
    | :? bool as c -> sb.Append(c) 
    | :? int as i -> sb.Append(i) 
    | :? float as i -> sb.Append(i) 
    | _ -> printObj currObj sb (depth + 1) 
+0

我一直在放> | ignore and returning() – PhilBrown 2010-06-09 13:46:01