2010-06-07 55 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