简体   繁体   English

F# Generic Map.count 使用反射

[英]F# Generic Map.count using Reflection

This is a follow-up on this previous question , but with a different twist.这是对上一个问题的跟进,但有不同的转折。

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.我想编写一个函数,给定一个对象 oMap,如果 oMap 恰好是Map<'k,'v>类型,则返回其计数,否则返回 -1。 My constraint : oMap type can only be 'discovered' at runtime.我的约束:oMap 类型只能在运行时“发现”。

As apparently "there is no built-in way to pattern match on a generic Map."显然“没有内置的方法可以在通用 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)

The error being :错误是:

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 : I am not looking for solutions where we know at compile time that oMap is of type Map<'k,'v> , eg : PS:我不是在寻找我们在编译时知道 oMap 是Map<'k,'v>类型的解决方案,例如:

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

== EDIT == == 编辑 ==

Thanks to Asti's suggestion, that's the solution that worked for me :感谢 Asti 的建议,这是对我有用的解决方案:

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.由于Map.count 刚刚定义为let count m = m.Count ,我们可以直接使用Count属性。

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 

Test:测试:

[<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

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.当您非常清楚您的值是什么类型时,您可以使用unbox装箱,除非该值被装箱。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM