2010-06-07 4 views
11

Ich versuche, rekursiv alle Eigenschaften eine Objekte auszudrucken und Sub-Typ-Eigenschaften etc. Meine Objektmodell wird wie folgt ...f # Muster mit Typen passend

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

Und meine Rekursion auf die basiert folgender Mustervergleich ...

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 

im Debugger sehe ich, dass currObj vom Typ String, int, float, usw., aber es springt immer auf den defualt Fall an der Unterseite. Irgendeine Idee, warum das passiert?

Antwort

4

ist hier, wie ich wurde es an die Arbeit ...

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

Sie möchten stattdessen etwas ähnliches.

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 

Dies wird von der MSDN-Dokumentation auf die Array Klasse kommen:

In .NET Framework Version 2.0, die Array-Klasse implementiert den System.Collections.Generic.IList, -System .Collections.Generic.ICollection, und System.Collections.Generic.IEnumerable generische Schnittstellen. Die Implementierungen werden den Arrays zur Laufzeit zur Verfügung gestellt und sind daher für die Dokumentation Build Tools nicht sichtbar . Als Ergebnis wird die generischen Schnittstellen erscheinen nicht in der Deklarationssyntax für die Array- Klasse, und es gibt keine Referenz Themen für Schnittstellenelemente, die durch Gießen ein Array den generischen Schnittstellentypen (explizite nur zugänglich sind Schnittstellenimplementierungen). Der Schlüssel Sache zu beachten, wenn Sie eine Array auf eine dieser Schnittstellen zu werfen ist , dass Mitglieder, die hinzufügen, einfügen oder Elemente entfernen NotSupportedException.

+0

der Compiler sagt mir: das Feld, Konstruktor oder Mitglied ‚GetValue‘ ist nicht – PhilBrown

+0

@philbrowndotcom definiert - ich glaube, Sie brauchen eine Besetzung zu tun. – ChaosPandion

0

Sind Sie sicher, dass sich das Programm nicht wie erwartet verhält? Die Debugger-Bereiche sind nicht immer zuverlässig.

+0

Das Problem ist, dass der enumer.Current.GetValue nicht wirklich der Wert ist, es ist ein PropertyInfo-Objekt. Es gibt hier eine Abstraktion, die mich verwirrt hat. – PhilBrown

+0

Also welche API verwenden Sie mit GetType/GetProperties/etc? Vielleicht erklären die Unterlagen dafür das? – Brian

1

In Ihrem Beispiel ist enumer.Current ein Objekt, das eine PropertyInfo enthält. Das bedeutet, dass currObj immer ein PropertyInfo-Objekt ist und immer dem letzten Fall in Ihrer Match-Anweisung entspricht.

Da Sie in der Art des Wert der Immobilie interessiert sind, müssen Sie die GetValue() -Methode der Propertyinfo rufen auf den aktuellen Wert der Immobilie zu erhalten (wie in ChaosPandion Antwort) .

Da ein Enumerator seine Werte als Objekte zurückgibt, müssen Sie enum.current auch auf eine PropertyInfo anwenden, bevor Sie auf GetValue zugreifen können.

Versuchen ersetzt

let currObj = (enumer.Current : obj) 

mit

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

Mit dieser Änderung ich Ihren Code (in FSI) an der Arbeit kann:

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

Wie andere hat darauf hingewiesen, Sie müssen das Element GetValue aufrufen, um den Wert der Eigenschaft abzurufen - die von Ihnen implementierte Iteration es über PropertyInfo Objekte, die "Deskriptoren der Eigenschaft" sind - nicht tatsächliche Werte. Allerdings verstehe ich nicht ganz, warum Sie GetEnumerator und while Schleife explizit verwenden, wenn die gleiche Sache mit for Schleife geschrieben werden kann.

Auch brauchen Sie nicht den Wert von dem sb.Append Aufruf zurückgegeben zu ignorieren - Sie können es einfach als das Gesamtergebnis zurückkehren (weil es ist dieStringBuilder). Dadurch wird der Code effizienter (weil er die Optimierung von Tail-Calls ermöglicht). Als letzten Punkt benötigen Sie nicht ToString in sb.Append(..), weil die Append Methode überlastet ist und für alle Standardtypen funktioniert.

So nach ein paar Vereinfachung, Sie so etwas wie diese bekommen kann (es ist nicht wirklich vor sich den depth Parameter, aber ich denke, man es für etwas verwenden wollen später):

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

Ich habe Putting |> Ignorieren und Returning() – PhilBrown