使用反射的F#通用Map.count [英] F# Generic Map.count using Reflection

查看:80
本文介绍了使用反射的F#通用Map.count的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是此上一页的后续文章问题,但有所不同.

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

推荐答案

由于Map.count

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屋!

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