【问题标题】:How to extract a Record from a Generic Dictionary?如何从通用词典中提取记录?
【发布时间】:2016-07-06 00:25:50
【问题描述】:

通过一些 API,我可以访问一系列 Gereric.Dictionary 形式的表格。因为该表有一个模式,所以我想从中提取记录。这是我有的?

open System.Collections.Generic

let gd = Dictionary<string,obj> ()
gd.Add("name", "Frank")
gd.Add("age", 24)
gd.Add("born", 1999)

type Person = { name : string
                age  : int}

let extractPerson (gd:Dictionary<string,obj>) = 
 { name = gd.["name"] :?> string
   age  = gd.["age"] :?> int}

我可以让函数extractPerson 更通用,比如

let extract<'T> (gd:Dictionary<string,obj>) =
  // ???

这样我就可以打电话了?

extract<Person> gd

【问题讨论】:

  • extract&lt;'T&gt; 如何工作?您是否有一种方法可以使用字典中的每个键在记录上设置同名属性?
  • 是的,只需用字典中的匹配键填充记录
  • F# 是静态类型的,因此您将不得不使用反射或DLR 之类的东西。如果您在编译时知道密钥列表,则可以查看代码生成或类型提供程序(但我怀疑是否有一个开箱即用的)。
  • 在我看来并不容易。假设'T 必须是一个记录,我会尝试通过反射通过'T 中的所有字段来解决它,从你的字典中读出字段,将它们收集在一个对象数组中,然后你必须调用记录构造函数。如果您的 'T 具有可设置的属性,事情会变得更容易。

标签: f#


【解决方案1】:

正如 cmets 中提到的,这只能使用反射来完成。以下适用于您的简单示例,但非常有限:

open Microsoft.FSharp.Reflection

let extract<'T>(gd:Dictionary<string,obj>) = 
  let flds = FSharpType.GetRecordFields(typeof<'T>)
  let vals = [| for f in flds -> gd.[f.Name] |]
  FSharpValue.MakeRecord(typeof<'T>, vals) :?> 'T

它使用GetRecordFields 找出字段的名称,然后从字典中获取它们的值并调用MakeRecord 来创建记录值。这不是超级高效,但根据您的需要,它可能对您来说足够好。

现在你可以按如下方式使用它:

let gd = Dictionary<string,obj> ()
gd.Add("name", "Frank")
gd.Add("age", 24)
gd.Add("born", 1999)

type Person = { name : string; age  : int}    
extract<Person> gd

【讨论】:

  • 感谢大家的大力帮助。在我看来,这似乎适用于所有记录类型,您的意思是根据性能受到限制吗?
  • 我认为它是有限的,因为您可能需要更多功能才能使其实用 - 嵌套记录怎么样?如果值为int 但您的记录需要float 怎么办?等等,但对于基础知识,这可以解决问题。
猜你喜欢
  • 2020-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 2020-08-29
相关资源
最近更新 更多