2010-06-07 12 views
11

Estoy intentando imprimir de forma recursiva a cabo todas las propiedades de los objetos y una propiedades de tipo sub etc. Mi modelo de objetos es la siguiente ...f # coincidencia de patrones con tipos

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 ; 
} 

Y mi recursividad se basa en la después de la coincidencia de patrones ...

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 

en el depurador veo que currObj es de tipo string, int, float, etc, pero siempre salta al caso defualt en la parte inferior. ¿Alguna idea de por qué está pasando esto?

Respuesta

4

Aquí es cómo tengo que trabajar ...

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

Desea algo como esto en su lugar.

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 

esto viene de la documentación de MSDN en la clase Array:

En la versión de .NET Framework 2.0, la clase matriz implementa el System.Collections.Generic.IList, Sistema .Collections.Generic.ICollection, y System.Collections.Generic.IEnumerable interfaces genéricas. Las implementaciones se proporcionan a las matrices en tiempo de ejecución y, por lo tanto, no son visibles para las herramientas de compilación de documentación . Como resultado, los genéricos interfaces no aparecen en la sintaxis declaración de la clase Array , y hay ninguna referencia temas para los miembros de interfaz que son accesible sólo por colada una matriz para el tipo de interfaz genérica (explícitos implementaciones de interfaz). La clave que hay que tener en cuenta cuando se lanza una matriz a una de estas interfaces es que los miembros que agregan, insertan o eliminan elementos lanzan NotSupportedException.

+0

el compilador me dice: el campo, constructor o miembro GetValue 'no está definido – PhilBrown

+0

@philbrowndotcom - Creo que hay que hacer un molde. – ChaosPandion

0

¿Seguro que el programa no se comporta como se esperaba? Los periodos de depuración no siempre son confiables.

+0

El problema es que el enumer.Current.GetValue no es realmente el valor, es un objeto PropertyInfo. Aquí hay algo de abstracción que me tiene confundido. – PhilBrown

+0

Entonces, ¿qué API estás usando con GetType/GetProperties/etc? Tal vez los documentos explican esto? – Brian

1

En su ejemplo, enumer.Current es un objeto que contiene un PropertyInfo. Esto significa que currObj es siempre un objeto PropertyInfo y siempre se corresponderá con el último caso en su declaración de coincidencia.

Puesto que usted está interesado en el tipo del valor de la propiedad, tendrá que llamar al método GetValue() de la PropertyInfo llegar al valor real de la propiedad (como en la respuesta de ChaosPandion) .

Dado que un Enumerator devuelve sus valores como objetos, también deberá convertir el enum.current en un PropertyInfo antes de poder acceder a GetValue.

intentar sustituir

let currObj = (enumer.Current : obj) 

con

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

Con este cambio, puedo obtener el código para trabajar (en el 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

Como otros han señalado, necesita invocar al miembro GetValue para obtener el valor de la propiedad, la iteración que implementó iterat es más de PropertyInfo objetos, que son "descriptores de la propiedad" - no valores reales. Sin embargo, no entiendo muy bien por qué está usando GetEnumerator y while loop explícitamente cuando lo mismo se puede escribir usando for loop.

Además, no es necesario pasar por alto el valor devuelto por la llamada sb.Append - simplemente puede retornarlo de acuerdo con el resultado global (porque es el StringBuilder). Esto realmente hará que el código sea más eficiente (porque habilita la optimización de llamadas de cola). Como último punto, no necesita ToString en sb.Append(..), porque el método Append está sobrecargado y funciona para todos los tipos estándar.

Así que después de unos cuantos simplificación, se puede obtener algo como esto (en realidad no es mediante el parámetro depth, pero supongo que desea utilizar para algo más adelante):

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

He estado poniendo |> ignorar y regresar() – PhilBrown

Cuestiones relacionadas