【发布时间】:2019-12-11 07:44:18
【问题描述】:
在previous post 中,我展示了如何在运行时使用 F# 反射从 单个 盒装 Map 对象 Map<'k,'v> 获取键。我试图扩展这个想法并传递一个 array 盒装 Map 对象 Map<'k,'v>[],但我找不到扩展原始单对象方法以将对象数组作为参数的方法。我想出了一个可行但看起来不正确的解决方案。我正在寻找一种更好、更惯用的方式。
在下面的代码中,keysFromMap 从单个装箱的Map<'k,'v> 参数中获取一组键; keysFromMapArray 是我第一次尝试从一个装箱的 Map<'k,'v>[] 参数做同样的事情 - 但它不起作用 - 并且 keysFromMapArrayWithCast 确实起作用,但必须在 FromMapArrayWithCast getter 级别进行向下转换似乎并不正确。
我从运行 keysFromMapArray 得到的错误信息(test2 被注释掉了):
{System.ArgumentException: Object of type 'System.Object[]' cannot be converted to type 'Microsoft.FSharp.Collections.FSharpMap`2[System.String,System.Int32][]'.
我的问题:为什么扩展 keysFromMap 方法以获取 Maps 参数数组不起作用,以及如何修复它以使其起作用?
示例代码:
module Example =
open System
let foo1 = [| ("foo11", 11); ("foo12", 12) |] |> Map.ofArray
let foo2 = [| ("foo21", 21); ("foo22", 22) |] |> Map.ofArray
type KeyGetter =
static member FromMap<'K, 'V when 'K : comparison>(map:Map<'K, 'V>) =
[| for kvp in map -> kvp.Key |]
static member FromMapArray<'K, 'V when 'K : comparison>(maps:Map<'K, 'V>[]) =
maps |> Array.map (fun mp -> [| for kvp in mp -> kvp.Key |]) |> Array.concat
static member FromMapArrayWithCast<'K, 'V when 'K : comparison>(omaps:obj[]) =
let typedmaps = [| for omp in omaps -> omp :?> Map<'K, 'V> |] // -- DOWNCASTING HERE --
typedmaps |> Array.map (fun mp -> [| for kvp in mp -> kvp.Key |]) |> Array.concat
let keysFromMap (oMap : obj) : obj[] =
let otype = oMap.GetType()
match otype.Name with
| "FSharpMap`2" ->
typeof<KeyGetter>.GetMethod("FromMap")
.MakeGenericMethod(otype.GetGenericArguments())
.Invoke(null, [| box oMap |]) :?> obj[]
| _ ->
Array.empty
let keysFromMapArray (oMaps : obj[]) : obj[] =
// skipped : tests to check that oMaps is not empty, and that all elements have the same type...
let otype = oMaps.[0].GetType()
match otype.Name with
| "FSharpMap`2" ->
typeof<KeyGetter>.GetMethod("FromMapArray")
.MakeGenericMethod(otype.GetGenericArguments())
.Invoke(null, [| box oMaps |]) :?> obj[] // -- FAILS HERE --
| _ ->
Array.empty
let keysFromMapArrayWithCast (oMaps : obj[]) : obj[] =
// skipped : tests to check that oMaps is not empty, and that all elements have the same type...
let otype = oMaps.[0].GetType()
match otype.Name with
| "FSharpMap`2" ->
typeof<KeyGetter>.GetMethod("FromMapArrayWithCast")
.MakeGenericMethod(otype.GetGenericArguments())
.Invoke(null, [| box oMaps |]) :?> obj[]
| _ ->
Array.empty
[<EntryPoint>]
let main argv =
printfn "#test1: keys from Map<'k,'v> - works"
let test = keysFromMap foo1
// printfn "#test2: keysFromArray from Map<'k,'v>[] - FAILS"
// let test = keysFromMapArray [| foo1; foo2 |]
printfn "#test3: keysFromArrayWithCast from obj[] - works"
let test = keysFromMapArrayWithCast [| foo1; foo2 |]
Console.ReadKey() |> ignore
0 // return exit code 0
【问题讨论】:
标签: reflection f#