f# pattern matching with types

后端 未结 5 1195
走了就别回头了
走了就别回头了 2021-01-01 20:31

I\'m trying to recursively print out all an objects properties and sub-type properties etc. My object model is as follows...

type suggestedFooWidget = {
            


        
相关标签:
5条回答
  • 2021-01-01 21:03

    In your example, enumer.Current is an object containing a PropertyInfo. This means that currObj is always a PropertyInfo object, and will always correspond to the last case in your match statement.

    Since you're interested in the type of the value of the property, you'll need to call the GetValue() method of the PropertyInfo to get to the actual value of the property (as in ChaosPandion's answer).

    Since an Enumerator returns its values as objects, you'll also need to cast the enum.current to a PropertyInfo before you can access GetValue.

    Try replacing

    let currObj = (enumer.Current : obj)
    

    with

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

    With this change, I can get your code to work (in FSI):

    >  let test = {authorId = 42; authorName = "Adams"; firm = {firmId = 1; firmName = "GloboCorp inc."} };;
    > string <| printObj test (new StringBuilder()) 1;;
    val it : string = "42Adams1GloboCorp inc."
    
    0 讨论(0)
  • 2021-01-01 21:04

    Here is how I got it to work...

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

    You want something like this instead.

    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
    

    This is coming from the MSDN docs on the Array class:

    In the .NET Framework version 2.0, the Array class implements the System.Collections.Generic.IList, System.Collections.Generic.ICollection, and System.Collections.Generic.IEnumerable generic interfaces. The implementations are provided to arrays at run time, and therefore are not visible to the documentation build tools. As a result, the generic interfaces do not appear in the declaration syntax for the Array class, and there are no reference topics for interface members that are accessible only by casting an array to the generic interface type (explicit interface implementations). The key thing to be aware of when you cast an array to one of these interfaces is that members which add, insert, or remove elements throw NotSupportedException.

    0 讨论(0)
  • 2021-01-01 21:07

    Are you sure the program is not behaving as expected? The debugger spans are not always reliable.

    0 讨论(0)
  • 2021-01-01 21:18

    As others has pointed out, you need to invoke the GetValue member to get the value of the property - the iteration that you implemented iterates over PropertyInfo objects, which are "descriptors of the property" - not actual values. However, I don't quite understand why are you using GetEnumerator and while loop explicitly when the same thing can be written using for loop.

    Also, you don't need to ignore the value returned by the sb.Append call - you can simply return it as the overall result (because it is the StringBuilder). This will actually make the code more efficient (because it enables tail-call optimizataion). As a last point, you don't need ToString in sb.Append(..), because the Append method is overloaded and works for all standard types.

    So after a few simplification, you can get something like this (it's not really using the depth parameter, but I guess you want to use it for something later on):

    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 讨论(0)
提交回复
热议问题