Reflection F#如何从装箱贴图对象中提取关键帧

Reflection F#如何从装箱贴图对象中提取关键帧,reflection,f#,Reflection,F#,这是和的后续行动 我需要编写一个函数,它接受一个对象(objtype)和一个键(也是一个objtype),如果该对象恰好是一个映射,即任何Map,则提取其键和值 困难在于我无法用泛型类型参数化函数,也无法对泛型类型上的对象进行模式匹配 我对F#Reflection不太熟悉,但一旦我知道了它的键,我就找到了一种获取地图值的方法。使用此示例代码: module TestItem = open System open Microsoft.FSharp.Reflection

这是和的后续行动

我需要编写一个函数,它接受一个对象(
obj
type)和一个键(也是一个
obj
type),如果该对象恰好是一个映射,即任何
Map
,则提取其键和值

困难在于我无法用泛型类型参数化函数,也无法对泛型类型上的对象进行模式匹配

我对F#Reflection不太熟悉,但一旦我知道了它的键,我就找到了一种获取地图值的方法。使用此示例代码:

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
现在,为了解决我的问题,我只需要找到一种从oMap对象中提取关键点的方法

我的问题:如果oMap确实是一个装箱映射对象,如何完成下面的代码以返回obj[]类型的oMap键

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

如果您有一个类型化的map
map
,一种方法是使用序列表达式在map上迭代,并使用您获得的
KeyValuePair
Key
属性获取密钥:

[| for kvp in map -> box kvp.Key |]
使用反射重构代码来实现这一点(与在另一个示例中调用
Item
的方式相同)将是一场噩梦。您可以做的一个很好的技巧是将其放入通用方法中:

type KeyGetter = 
  static member GetKeys<'K, 'V when 'K : comparison>(map:Map<'K, 'V>) = 
    [| for kvp in map -> box kvp.Key |]

这很有效。但是,我要补充的是,事实上,你需要这样做是一个信号,表明你的系统很可能不是很好的设计,所以我会考虑改变你的应用程序的设计,这样你就不需要做这样的事情。当然,这样做有一些很好的理由,但它不应该太常见。

如果您有一个类型化的map
map
,一种方法是使用序列表达式在map上迭代,并使用您得到的
KeyValuePair
属性获取密钥:

[| for kvp in map -> box kvp.Key |]
使用反射重构代码来实现这一点(与在另一个示例中调用
Item
的方式相同)将是一场噩梦。您可以做的一个很好的技巧是将其放入通用方法中:

type KeyGetter = 
  static member GetKeys<'K, 'V when 'K : comparison>(map:Map<'K, 'V>) = 
    [| for kvp in map -> box kvp.Key |]

这很有效。但是,我要补充的是,事实上,你需要这样做是一个信号,表明你的系统很可能不是很好的设计,所以我会考虑改变你的应用程序的设计,这样你就不需要做这样的事情。当然,这样做有一些很好的理由,但不应该太常见。

这将以字符串数组的形式返回键

    let keys (oMap : obj) =
        let otype = oMap.GetType()

        match otype.Name with
        | "FSharpMap`2" -> 
            printfn "  -Map object identified"

            (* COMPLETE HERE *)
            let map = oMap :?> Map<string, Bar>
            let keys = map |> Map.toArray |> Array.map fst
            keys
        | _             ->  
            printfn "  -Not a Map object"
            Array.empty // return empty array

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

        let keys = keys baz

        Console.ReadKey() |> ignore
        0 // return exit code 0
let键(oMap:obj)=
让otype=oMap.GetType()
将otype.Name与
|“FSharpMap`2”->
printfn“-已识别映射对象”
(*在此处填写*)
设map=oMap:?>map
让keys=map |>map.toArray |>Array.map fst
钥匙
| _             ->  
printfn“-不是贴图对象”
Array.empty//返回空数组
[]
让主argv=
打印fn“#使用正确的键进行测试”
让试验=项目baz“bar1”
printfn“\n#使用不正确的键进行测试”
让试验=项目baz“bar1X”
让keys=keys baz
Console.ReadKey()|>忽略
0//返回退出代码0

这将以字符串数组的形式返回键

    let keys (oMap : obj) =
        let otype = oMap.GetType()

        match otype.Name with
        | "FSharpMap`2" -> 
            printfn "  -Map object identified"

            (* COMPLETE HERE *)
            let map = oMap :?> Map<string, Bar>
            let keys = map |> Map.toArray |> Array.map fst
            keys
        | _             ->  
            printfn "  -Not a Map object"
            Array.empty // return empty array

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

        let keys = keys baz

        Console.ReadKey() |> ignore
        0 // return exit code 0
let键(oMap:obj)=
让otype=oMap.GetType()
将otype.Name与
|“FSharpMap`2”->
printfn“-已识别映射对象”
(*在此处填写*)
设map=oMap:?>map
让keys=map |>map.toArray |>Array.map fst
钥匙
| _             ->  
printfn“-不是贴图对象”
Array.empty//返回空数组
[]
让主argv=
打印fn“#使用正确的键进行测试”
让试验=项目baz“bar1”
printfn“\n#使用不正确的键进行测试”
让试验=项目baz“bar1X”
让keys=keys baz
Console.ReadKey()|>忽略
0//返回退出代码0

类型匹配是类型系统中不正确的一部分。你不太可能需要这么做。编译器之外的正确F#代码不使用
Microsoft.FSharp.Reflection
GetType()
。这些东西是公开给用户的,允许用户在类型系统中工作,但这是一个错误(.Net的错误多于F#的错误)。类型匹配是类型系统中不合适的部分。你不太可能需要这么做。编译器之外的正确F#代码不使用
Microsoft.FSharp.Reflection
GetType()
。用户可以在类型系统中使用这些东西,但这是一个错误(.Net的错误多于F#)。“当然,这样做有一些很好的理由”。我非常怀疑。你能给出一些吗?任何类型的实体泛型库,库作者都不知道用户传入的类型,例如解析器、序列化器、数据库访问。是的,@JSparrow说的和我想的差不多-自动数据库接口或某种序列化对于这样的事情来说是一个合理的地方(但是,你可能需要一种使用Reflection.Emit的更有效的方法)。@Tomas Petricek。我没有意识到
KeyValuePair
类型,我将不得不花更多的时间在你的反射技巧上,以便更好地理解它,但它工作得很好,谢谢。(由于评论大小的限制,我不得不将我的其他评论分成几个较小的小节).4-…因此,我尝试使用F#反射绕过F#type系统。当Excel的替代品变成强类型时,我将是第一个跳槽的人,但实际上还需要一段时间。我很想知道您是否认为这个用例可能是您提到的“好的理由”之一?谢谢您的帮助。“当然,做这样的事情有一些很好的理由。”我非常怀疑。你能给出一些理由吗?有吗