F#如何从装箱的Map对象提取键

时间:2019-12-09 13:09:34

标签: reflection f#

这是this postthat post的跟进活动。

我需要编写一个函数,该函数带有一个对象(obj类型和一个键(也是obj类型),如果对象碰巧是Map,则可以是任何{{ 1}}然后提取其键和值。

困难在于我无法使用泛型类型对函数进行参数化,也无法对泛型类型上的对象进行模式匹配。

我对F#Reflection不熟悉,但是一旦知道Map的键,我就找到了一种获取Map值的方法。使用此示例代码:

Map<'k,'v>

运行上面的代码会将以下内容输出到控制台:

module TestItem = 
    open System
    open Microsoft.FSharp.Reflection

    // some uninteresting types for this example, could be anything arbitrary
    type Foo = {argF1 : string; argF2 : double; argF3 : bool[]}
    type Bar = {argB1 : string; argB2 : double; argB3 : Foo[]}

    // and their instances
    let foo1 = {argF1 = "foo1"; argF2 = 1.0; argF3 = [| true  |]}
    let foo2 = {argF1 = "foo2"; argF2 = 2.0; argF3 = [| false |]}

    let bar1 = {argB1 = "bar1"; argB2 = 10.0; argB3 = [| foo1 |]}
    let bar2 = {argB1 = "bar2"; argB2 = 20.0; argB3 = [| foo2 |]}

    // a Map type
    type Baz = Map<String,Bar>    
    let baz : Baz = [| ("bar1", bar1); ("bar2", bar2) |] |> Map.ofArray

    let item (oMap : obj) (key : obj) : unit =
        let otype = oMap.GetType()

        match otype.Name with
        | "FSharpMap`2" -> 
            printfn "  -Map object identified"
            let prop  = otype.GetProperty("Item")

            try
                let value = prop.GetValue(oMap, [| key |]) 
                printfn "  -Value associated to key:\n %s" (value.ToString())
            with
            | _             ->  
                printfn "  -Key missing from oMap"
        | _             ->  
            printfn "  -Not a Map object"

    [<EntryPoint>]
    let main argv =
        printfn "#test with correct key"
        let test = item baz "bar1"

        printfn "\n#test with incorrect key"
        let test = item baz "bar1X"

        Console.ReadKey() |> ignore
        0 // return exit code 0

现在,要解决我的问题,我只需要找到一种从oMap对象中提取键的方法。

我的问题:如果oMap确实是盒装Map对象,如何完成下面的代码以返回obj []类型的oMap键?

#test with correct key
  -Map object identified
  -Value associated to key:
 {argB1 = "bar1";
 argB2 = 10.0;
 argB3 = [|{argF1 = "foo1";
            argF2 = 1.0;
            argF3 = [|true|];}|];}

#test with incorrect key
  -Map object identified
  -Key missing from oMap

2 个答案:

答案 0 :(得分:6)

如果您有一个类型化的映射map,执行此操作的一种方法是使用序列表达式遍历该映射并使用Key的{​​{1}}属性获取键您得到:

KeyValuePair

使用反射(在另一个示例中调用[| for kvp in map -> box kvp.Key |] 的方式)来重构代码以实现此目的将是一场噩梦。您可以做的一个不错的技巧是将其放入通用方法中:

Item

现在,您可以通过反射访问type KeyGetter = static member GetKeys<'K, 'V when 'K : comparison>(map:Map<'K, 'V>) = [| for kvp in map -> box kvp.Key |] 方法,获取GetKeys的类型参数并将其用作方法的Map'K,然后调用以您的'V作为参数的方法:

oMap

这有效。但是,我还要补充一句,实际上您实际上需要执行此操作,这表明您的系统很可能不是完全正确设计的,因此,我将考虑更改应用程序的设计,从而使您无需执行此类操作的东西。当然,这样做有一些很好的理由,但这应该不太常见。

答案 1 :(得分:2)

这会将键作为字符串数组返回。

    let keys (oMap : obj) =
        let otype = oMap.GetType()

        match otype.Name with
        | "FSharpMap`2" -> 
            printfn "  -Map object identified"

            (* COMPLETE HERE *)
            let map = oMap :?> Map<string, Bar>
            let keys = map |> Map.toArray |> Array.map fst
            keys
        | _             ->  
            printfn "  -Not a Map object"
            Array.empty // return empty array

    [<EntryPoint>]
    let main argv =
        printfn "#test with correct key"
        let test = item baz "bar1"

        printfn "\n#test with incorrect key"
        let test = item baz "bar1X"

        let keys = keys baz

        Console.ReadKey() |> ignore
        0 // return exit code 0