使用反射的F#通用Map.count [英] F# Generic Map.count using Reflection
问题描述
这是此上一页的后续文章问题,但有所不同.
This is a follow-up on this previous question, but with a different twist.
我想编写一个函数,给定一个对象oMap,如果oMap恰好是Map<'k,'v>
类型,则返回其计数,否则返回-1.我的约束:oMap类型只能在运行时发现".
I would like to write a function which, given an object oMap, returns its count if oMap happens to be of type Map<'k,'v>
, and -1 otherwise. My constraint : oMap type can only be 'discovered' at runtime.
显然,在通用地图上没有内置的模式匹配模式". (请参阅上一个问题的链接),对此我正在使用反射.
As apparently "there is no built-in way to pattern match on a generic Map." (see link to previous question), I am using reflection for this.
namespace genericDco
module Test1 =
let gencount (oMap : obj) : int =
let otype = oMap.GetType()
let otypenm = otype.Name
if otypenm = "FSharpMap`2" then
// should work, as oMap of type Map<'a,'b>, but does not. *How to fix this?*
Map.count (unbox<Map<_,_>> oMap)
else
// fails, as oMap is not of any type Map<'a,'b>.
-1
let testfailObj : int = gencount ("foo")
// FAILS
let testsuccessObj : int =
let oMap = [| ("k1", "v1"); ("k1", "v1") |] |> Map.ofArray
gencount (box oMap)
错误为:
System.InvalidCastException: Unable to cast object of type 'Microsoft.FSharp.Collections.FSharpMap`2[System.String,System.String]' to type 'Microsoft.FSharp.Collections.FSharpMap`2[System.IComparable,System.Object]'. at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
我的问题:我应该如何重写上面的内容才能使它正常工作?
My question: How should I rewrite the above to get this to work?
PS:我不是在寻找在编译时知道oMap的类型为Map<'k,'v>
的解决方案,例如:
PS : I am not looking for solutions where we know at compile time that oMap is of type Map<'k,'v>
, e.g. :
module Test2 =
let gencount2<'k,'v when 'k : comparison> (gMap : Map<'k,'v>) : int =
Map.count gMap
let testsuccessStr : int =
let gMap = [| ("k1", "v1"); ("k2", "v2") |] |> Map.ofArray
gencount2<string,string> gMap
let testsuccessDbl : int =
let gMap = [| ("k1", 1.0); ("k2", 2.0); ("k3", 3.0) |] |> Map.ofArray
gencount2<string,double> gMap
==编辑==
感谢阿斯蒂的建议,这是对我有用的解决方案:
Thanks to Asti's suggestion, that's the solution that worked for me :
let gencount (oMap : obj) : int =
let otype = oMap.GetType()
let propt = otype.GetProperty("Count")
try
propt.GetValue(oMap) :?> int
with
| _ -> -1
推荐答案
Since Map.count
is just defined as let count m = m.Count
, we can just go for the Count
property.
let gencount<'k,'v when 'k : comparison> map =
let mtype = typeof<Map<'k, 'v>>
let propt = mtype.GetProperty("Count")
if map.GetType() = mtype then
propt.GetValue(map) :?> int
else
-1
测试:
[<EntryPoint>]
let main argv =
let m = Map.ofSeq [ ("a", 1); ("b", 2)]
printfn "%d" (gencount<string, int> m)
printfn "%d" (gencount<string, string> m)
Console.ReadKey() |> ignore
0 // return exit code 0
如果没有其他约束信息,使用
代替类型将简单地以_
结尾.当您强烈知道您的值是什么类型时,可以使用unbox
,除了将值装在框中.
Using _
in place of a type will simply end up as object if no additional constraint information is available. You use unbox
when you strongly know what type your value is, except that the value is boxed in.
这篇关于使用反射的F#通用Map.count的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!