为什么F#map使用可变操作实现接口?

时间:2012-06-20 13:03:21

标签: dictionary map f# immutability

当我意识到F#的map同时实现了IDictionary&lt;'Key,'Value&gt;和ICollection<KeyValuePair<'a, 'b>>考虑到两者都支持变异(添加和删除)作为合同的一部分。

看看map的实现,当你试图引起变异时,它就会超越!

let map = [| (1, "one"); (2, "two") |] |> Map.ofArray
let dict = map :> IDictionary<int, string>
dict.Add(3, "three");;

以上代码抛出异常:

  

System.NotSupportedException:无法突变地图值。在   Microsoft.FSharp.Collections.FSharpMap 2.System-Collections-Generic-IDictionary -2-添加(TKEY的   k,TValue v)at。$ FSI_0007.main @()停止   由于错误

这是预期的。

对于一个不可变的集合,它能够将自己暴露为一个可变的集合,只是当该集合的使用者试图导致变异时抛出异常似乎是一个危险的决定。

我在这里错过了什么吗?

3 个答案:

答案 0 :(得分:9)

我认为主要原因是.NET没有任何代表不可变(一个接口的一部分)字典的接口。这意味着所有.NET API都必须将IDictionary<K, V>作为参数,即使它们只打算从字典中读取。所以:

  • 实现IDictionary<'K, 'V>几乎是使F#不可变映射可用作任何需要支持查找的对象的.NET库的参数的唯一方法。遗憾的是,.NET中没有只读替代方案。

  • 实现ICollection<KeyValuePair<'K, 'V>>对我来说没有多大意义,因为有一个只读替代IEnumerable<KeyValuePair<'K, 'V>>,而且不可变映射也实现了这个接口。

    但是也许有一些.NET库采用ICollection<'T>(为了有效 - 即获取Count而不枚举所有元素)并以只读方式使用它。

    编辑:正如Daniel在评论中所指出的,实现ICollection是必需的,因为IDictionary接口继承了它。

我认为缺少只读接口是非常不幸的,但可能无法解决这个问题,因为这意味着要改变现有的.NET集合库。

答案 1 :(得分:6)

IsReadOnly允许接口为只读,即使它提供了可写方法。

答案 2 :(得分:4)

这是一个.Net的事情。您不能仅实现某些接口,但他们希望实现IDictionary`2接口 - 因为您可以使用其查询和转换方法(因此您可以将其传递给从IDictionary`2读取的函数)