[英]f# pattern matching with types
我正在尝试以递归方式打印出所有对象属性和子类型属性等。我的对象模型如下...
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的类型为string,int,float等,但它始终跳转到底部的defualt情况。 知道为什么会这样吗?
正如其他人所指出的那样,您需要调用GetValue
成员来获取属性的值 - 您实现的迭代迭代PropertyInfo
对象,这些对象是“属性的描述符” - 而不是实际值。 但是,我不太明白为什么你使用GetEnumerator
和while
循环时可以使用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)
这是我如何让它工作......
let getMethod = prop.GetGetMethod()
let value = getMethod.Invoke(o, Array.empty)
ignore <|
match value with
| :? float as f -> sb.Append(f.ToString() + ", ") |> ignore
...
在您的示例中, enumer.Current
是一个包含PropertyInfo的对象。 这意味着currObj始终是PropertyInfo对象,并且始终对应于match语句中的最后一个case。
由于您对属性值的类型感兴趣,因此您需要调用PropertyInfo的GetValue()方法来获取属性的实际值(如ChaosPandion的答案)。
由于Enumerator将其值作为对象返回,因此您还需要在访问GetValue之前将enum.current转换为PropertyInfo。
尝试更换
let currObj = (enumer.Current : obj)
同
let currObj = unbox<PropertyInfo>(enumer.Current).GetValue (o, null)
通过此更改,我可以使您的代码工作(在FSI中):
> let test = {authorId = 42; authorName = "Adams"; firm = {firmId = 1; firmName = "GloboCorp inc."} };;
> string <| printObj test (new StringBuilder()) 1;;
val it : string = "42Adams1GloboCorp inc."
你需要这样的东西。
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
这来自Array
类的MSDN文档:
在.NET Framework 2.0版中,Array类实现System.Collections.Generic.IList,System.Collections.Generic.ICollection和System.Collections.Generic.IEnumerable通用接口。 这些实现在运行时提供给数组,因此文档构建工具不可见。 因此,通用接口不会出现在Array类的声明语法中,并且没有可通过将数组转换为通用接口类型(显式接口实现)来访问的接口成员的参考主题。 将数组转换为其中一个接口时要注意的关键是添加,插入或删除元素的成员会抛出NotSupportedException。
你确定程序没有按预期运行吗? 调试器跨度并不总是可靠的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.