我需要编写一个函数,它接受一个对象(obj类型)和一个键(也是一个obj类型),如果对象恰好是一个 Map,Map<'k,'v>那么它是 any,然后提取它的键和值。
困难在于我无法使用泛型类型对函数进行参数化,并且我们无法对泛型类型的对象进行模式匹配。
我不熟悉 F# 反射,但是一旦我知道它的键,我就找到了一种获取 Map 值的方法。使用此示例代码:
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
Run Code Online (Sandbox Code Playgroud)
运行上面的代码会向控制台输出以下内容:
#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
Run Code Online (Sandbox Code Playgroud)
现在,为了解决我的问题,我只需要找到一种方法来从 oMap 对象中提取键。
我的问题:如何完成下面的代码以返回 obj[] 类型的 oMap 键,如果 oMap 确实是一个装箱的 Map 对象?
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
Run Code Online (Sandbox Code Playgroud)
如果你有一个类型化的 map map,这样做的一种方法是使用序列表达式迭代 map 并使用你得到的Key属性获取键KeyValuePair:
[| for kvp in map -> box kvp.Key |]
Run Code Online (Sandbox Code Playgroud)
使用反射(与您Item在其他示例中调用的方式相同)重建代码以执行此操作将是一场噩梦。您可以做的一个很好的技巧是将其放入通用方法中:
type KeyGetter =
static member GetKeys<'K, 'V when 'K : comparison>(map:Map<'K, 'V>) =
[| for kvp in map -> box kvp.Key |]
Run Code Online (Sandbox Code Playgroud)
现在,您可以GetKeys通过反射访问该方法,获取您的类型参数Map并使用这些作为'K和'V该方法的类型参数,并使用您的oMap作为参数调用该方法:
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
Run Code Online (Sandbox Code Playgroud)
这有效。但是,我应该补充一点,您实际上需要这样做的事实表明您的系统很可能没有完全设计好,因此我会考虑更改您的应用程序的设计,以便您不需要这样做的东西。当然,做这样的事情有一些很好的理由,但它不应该太普遍。
| 归档时间: |
|
| 查看次数: |
309 次 |
| 最近记录: |