需要惊喜转换从 F# dict 初始化 MongoDB 文档?

Reb*_*bin 0 clr f# mongodb bson

这里有一个微观问题,关于为什么我想出的最终答案中需要向上转换(在本文的底部);以及一个关于我是否错过了“房间里的大象”的宏观问题:一些非常明显的简洁方式来做我想做的事[请不要问我-为什么-我想要我真正想要的;只要把它当作我想要这个的给定,它就是......]

我想通过 MongoDB.Bson CLR 程序集从 F# 初始化 BsonDocument。我认为我应该使用的 BsonDocument 构造函数的特定重载是

MongoDB.Bson.BsonDocument.BsonDocument(IDictionary<string,object>)
Run Code Online (Sandbox Code Playgroud)

这就是为什么我认为这是我应该使用的(以下是在类型花园中的漫长漫步......)

MongoDB 站点MongoDB CSharp 驱动程序教程中的 C# 示例使用集合初始值设定项语法,该语法映射到 BsonDocument 公开的接口上的 .Add 的一个或多个调用。教程示例类似于以下内容:

var bdoc = new BsonDocument { { "a", "1" }, { "b", "2" }, };
Run Code Online (Sandbox Code Playgroud)

我不确定正在使用 .Add 的哪个重载(并且不知道如何在 Visual Studio 中检查),但所有基于字典的重载都键入为 <string, object>。在这种情况下,每对中的第二个值,即字符串类型的“1”和“2”,自动(通过继承)也是对象类型,因此一切正常。.Add 的其他重载要求第二项的类型为 BsonValue,它是 BsonString 的抽象超类型,无论使用哪种重载,它都具有从 .NET 字符串的隐式转换;所以那里也一切都好。调用构造函数的哪个重载并不重要。

这有点难以转化为 F# 等效项,因为很难获取 BsonDocument 的 .Add 方法。我想到了

[("a", "1");("b", "2");] |> Seq.iter BsonDocument.Add
Run Code Online (Sandbox Code Playgroud)

但这不起作用,因为 BsonDocument.Add 不是静态方法;我可以实例化 BsonDocument,然后编写一个调用 BsonDocument 的 .Add 方法的 fun lambda,这至少会隔离 fun 的可变性:

[("a", "1");("b", "2");] |> Seq.fold ...
Run Code Online (Sandbox Code Playgroud)

但事实证明这非常丑陋,因为在 BsonDocument 上需要显式类型表示法很有趣,因为引用 BsonDocument 的变量出现在 (new BsonDocument()) 之前,所以从左到右的类型推断不会有足够的信息(还),并且因为乐趣(至少,显然)无法知道它应该访问每对中第二个值从字符串到 BsonString 的隐式转换...

let bdoc = [("a","1");("b","2");] |> Seq.fold (fun (doc:BsonDocument) pair -> doc.Add(fst pair, new BsonString(snd pair))) (new BsonDocument())
Run Code Online (Sandbox Code Playgroud)

...无论如何,我想,我会使用构造函数的更大重载

BsonDocument(IDictionary<string, object>)
Run Code Online (Sandbox Code Playgroud)

但这是被迫的:

let bdoc = (new BsonDocument(dict
  [("a", "1" :> Object); 
   ("b", "2" :> Object);
  ]))
Run Code Online (Sandbox Code Playgroud)

如果我去掉 upcasts

:> Object
Run Code Online (Sandbox Code Playgroud)

然后 F# 抱怨它找不到 BsonDocument 的重载。

(花园里的漫长漫步结束了……)

毕竟,微观问题是,为什么在 F# 中,它不能弄清楚输入字典中的“1”和“2”是对象,从而找到合适的重载?

更大的宏观问题是我是否错过了在 F# 中执行此操作的适当、最佳实践、超酷、简洁的方法?

Seb*_*ood 5

这不是 MongoDB 问题。问题是这("1", "2")是 a string * string,因此您正在IDictionary<string,string>使用dict构造函数创建 a 。F# 已推断出您指定的类型。在这种情况下,它的类型推断不会确定您的意思obj。因此你必须告诉它。

> dict
  [("a", "1" :> obj); 
   ("b", "2" :> obj);
  ];;
val it : System.Collections.Generic.IDictionary<string,obj> =
  seq [[a, 1] {Key = "a";
               Value = "1";}; [b, 2] {Key = "b";
                                      Value = "2";}]
> dict
  [("a", "1"); 
   ("b", "2");
  ];;
val it : System.Collections.Generic.IDictionary<string,string> =
  seq [[a, 1] {Key = "a";
               Value = "1";}; [b, 2] {Key = "b";
                                      Value = "2";}]
Run Code Online (Sandbox Code Playgroud)

  • 请注意,例如 `("a", box "1")` 比 `("a", "1" :&gt; obj)` 更简洁。 (3认同)
  • [此处](http://stackoverflow.com/questions/7407873/concisely-creating-an-idictionary-obj/7410842#7410842)是一个更简洁的解决方案。 (2认同)