F#如何从装箱的Map对象中提取键 [英] F# How to extract keys from a boxed Map object

查看:86
本文介绍了F#如何从装箱的Map对象中提取键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是的后续内容发布.

我需要编写一个函数,该函数带有一个对象(obj类型)和一个键(也是一个obj类型),如果该对象恰好是Map,则任何Map<'k,'v>然后提取其键和值.

I need to write a function which takes an object (obj type) and a key (also an obj type), and if the object happens to be a Map, that is any Map<'k,'v> then extracts its keys and values.

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

The difficulty is that I cannot parametrize the function with generic types and that we cannot pattern-match objects on generic types.

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

I am not familiar with F# Reflection, but I found a way to get the Map's values, once I know its keys. With this example code :

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

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

Running the code above ouputs the following to the Console :

#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

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

Now, to solve my problem, I would just need to find a way to extract the keys from the oMap object.

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

My question : how to complete the code below to return the oMap keys, of type obj[], if oMap is indeed a boxed Map object?

module CompleteThis =
    open System
    open Microsoft.FSharp.Reflection

    let keys (oMap : obj) (key : obj) : obj[] =
        let otype = oMap.GetType()

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

            (* COMPLETE HERE *)
            Array.empty // dummy
        | _             ->  
            printfn "  -Not a Map object"
            Array.empty // return empty array

推荐答案

如果您有类型映射图map,执行此操作的一种方法是使用序列表达式遍历该图并使用KeyValuePair的>属性:

If you have a typed map map, one way of doing this is to iterate over the map using a sequence expression and get the keys using the Key property of the KeyValuePair that you get:

[| for kvp in map -> box kvp.Key |]

使用反射(在另一个示例中调用Item的方式相同)来重构代码以完成此操作将是一场噩梦.您可以做的一个不错的技巧是将其放入通用方法中:

Reconstructing the code to do this using reflection (in the same way in which you invoke Item in your other example) would be a nightmare. A nice trick that you can do is to put this into a generic method:

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的身份调用该方法一个参数:

Now, you can access the GetKeys method via reflection, get the type arguments of your Map and use those as 'K and 'V of the method, and invoke the method with your oMap as an argument:

let keys (oMap : obj) : obj[] =
    let otype = oMap.GetType()    
    match otype.Name with
    | "FSharpMap`2" ->          
        typeof<KeyGetter>.GetMethod("GetKeys")
            .MakeGenericMethod(otype.GetGenericArguments())
            .Invoke(null, [| box oMap |]) :?> obj[]
    | _             ->  
        Array.empty

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

This works. However, I should add that I the fact that you actually need to do this is a sign that your system is most likely not exactly well designed, so I would consider changing the design of your application so that you do not need to do this kind of thing. There are, of course, some good reasons for doing something like this, but it should not be too common.

这篇关于F#如何从装箱的Map对象中提取键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆