我有类似的声明:
[<DataContract>]
type Defect =
{
[<field: DataMember(Name="referenceId")>]
Referenceid : string
[<field: DataMember(Name="comment")>]
Comment : string
[<field: DataMember(Name="start")>]
Start : DateTime
[<field: DataMember(Name="finish")>]
Finish : DateTime
}
此类型有4个字段。我有4个元素的列表。
我有其他类型的n
字段。我有n
个元素的列表。
是否有通用的方法从列表中创建我的类型的实例,所以列表中的每个元素都将是该类型的对应字段?
答案 0 :(得分:5)
您可以使用反射创建记录类型的实例。请考虑以下代码:
let make<'record> (values: obj []) =
let typ = typeof<'record>
let fieldInfos = FSharpType.GetRecordFields(typ)
let orderedValues =
// here you might need to reorder values using fieldInfos
...
FSharpValue.MakeRecord(typ, values) :?> 'record
唯一可能棘手的问题是以正确的顺序获取值,以便它们最终出现在正确的字段中。
我相信你想要的顺序是声明的顺序,但如果列表中的值标有字段名称,你可能想要实现那个排序逻辑,只是为了安全起见。
答案 1 :(得分:3)
除非你选择基于反射的编程,或者输入所有内容为obj
,否则我很确定没有任何自动方式来做这样的事情。
考虑OP中的Defect
类型。在结构上,它有两个string
元素和两个DateTime
元素。这与元组类型密切相关:
string * string * DateTime * DateTime
记录类型和元组之间的差异是:
换句话说,即使元素的顺序与原始类型中的声明顺序不同,此表达式仍会编译为Defect
值:
let d = {
Finish = DateTime.Now
Comment = "foo"
Start = (DateTime 42L)
Referenceid = "bar" }
您仍然可以按照定义命名元素的顺序写出记录值:
let d' = {
Referenceid = "bar"
Comment = "foo"
Start = (DateTime 42L)
Finish = DateTime.Now }
在结构上,此值等同于此元组:
let t = ("bar", "foo", DateTime 42L, DateTime.Now)
到目前为止,应该清楚OP问题等同于常见问题:How can I convert between list and tuple?
简短的回答是:你不能。
这些类型结构不同。
给定Defect
类型,等效列表的类型是什么?
它不能string list
,因为它不可能包含DateTime
值。它不能DateTime list
,因为它可能包含string
值。
通过定义一个被歧视的联盟来解决类型问题 :
type DefectElement = Text of string | Time of DateTime
let l = [Text "bar"; Text "foo"; Time (DateTime 42L); Time DateTime.Now]
现在l
的类型为DefectElement list
。但是,这并没有解决问题,因为它的不能保证按顺序包含正好四个元素。
以下是DefectElement list
类型的其他一些有效值:
[Text "bar"; Time (DateTime 42L); Text "foo"; Time DateTime.Now]
[Text "bar"; Time (DateTime 42L); Text "foo"; Time DateTime.Now; Text "baz"]
[Text "bar"; Time (DateTime 42L); Text "foo"]
[Text "bar"; Text "foo"; Text "foo"; Text "bar"]
List.empty<DefectElement>
您会注意到 none 中的 none 可以有意义地或明确地解释为与Defect
对应的值。