如何在F#中实现Generic.Dictionary的扩展方法(类型约束)

时间:2012-12-05 10:57:32

标签: f#

  

可能重复:
  Type extension errors

我想在F#中添加一个扩展方法到System.Collections.Generic.Dictionary。麻烦的是我似乎无法正确地获得类型约束。我希望以下内容能起作用:

type Dictionary<'k, 'd when 'k : equality> with

   static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =
       let res = new Dictionary<'k, 'd> ()
       for (k, d) in xs do
           res.Add (k, d)
       res

但是,编译器抱怨我的声明与Dictionary的声明不同。当我省略等式约束时,它不会产生那个特定的错误。但随后它警告它失踪了。非常感谢任何提示,最好是其他“提高警告级别”: - )

修改

非常感谢KVB提供我想要的答案。

type Dictionary<'k, 'd> with

static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =

    let res = new Dictionary<'k, 'd> (EqualityComparer<'k>.Default)
    for (k, d) in xs do
        res.Add (k, d)
    res

编辑:以下是更好地解释我对RJ的回复的示例。它表明,在实例化类型时类型参数是可选的,前提是编译器可以推断它们。它编译时没有警告或错误。

type System.Collections.Generic.Dictionary<'k, 'd> with
   static member test (dict:System.Collections.Generic.Dictionary<'k, 'd>) : bool =
        dict.Values |> List.ofSeq |> List.isEmpty


let test (x:System.Collections.Generic.Dictionary<'k, 'd>) =
    System.Collections.Generic.Dictionary.test x

3 个答案:

答案 0 :(得分:5)

由于某种原因,类型参数的名称必须匹配 - 这对我来说很好用

open System.Collections.Generic
type Dictionary<'TKey, 'TValue>  with
   static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =
       let res = new Dictionary<'k, 'd> ()
       for (k, d) in xs do
           res.Add (k, d)
       res

我不知道为什么会出现这种情况(30秒看看规范也没有提供任何线索)。

更新 - 错误实际上是Dictionary参数与方法中写入的相同 - 执行

type Dictionary<'a, 'b>  with
   static member ofList (xs:list<'k * 'd>) : Dictionary<'k, 'd> =
       let res = new Dictionary<'k, 'd> ()
       for (k, d) in xs do
           res.Add (k, d)
       res

工作得很好。这实际上现在是有道理的。当参数相同时,由于'k:equality,还有一个未指定的约束 - new Dictionary<'k,'d>。但是,出于某种原因,我们不能在扩展定义中放置约束(避免重复?),因此存在错误。

答案 1 :(得分:3)

如果您需要各种集合的ofSeq函数,您可能会考虑类似于C#集合初始值设定项的方法。也就是说,使用Add方法使其适用于任何集合。这也回避了你目前的问题。

open System.Collections.Generic
open System.Collections.Concurrent

module Dictionary =
  let inline ofSeq s = 
    let t = new ^T()
    for k, v in s do
      (^T : (member Add : ^K * ^V -> ^R) (t, k, v)) |> ignore
    t

module Collection =
  let inline ofSeq s = 
    let t = new ^T()
    for v in s do
      (^T : (member Add : ^V -> ^R) (t, v)) |> ignore
    t

open Dictionary

let xs = List.init 9 (fun i -> string i, i)
let d1 : Dictionary<_,_> = ofSeq xs
let d2 : SortedDictionary<_,_> = ofSeq xs
let d3 : SortedList<_,_> = ofSeq xs

open Collection

let ys = List.init 9 id
let c1 : ResizeArray<_> = ofSeq ys
let c2 : HashSet<_> = ofSeq ys
let c3 : ConcurrentBag<_> = ofSeq ys

有趣的是,您甚至可以将其限制为具有特定构造函数重载的集合类型。例如,如果您想使用结构相等,则可以执行以下操作:

let t = (^T : (new : IEqualityComparer< ^K > -> ^T) (HashIdentity.Structural))

答案 2 :(得分:0)

您将无法删除警告,因为您指的是不存在的类型,Dictionary而不是Dictionary<_,_>。如果您想要访问它,可以创建一个Dictionary模块。

open System.Collections.Generic

type Dictionary<'a,'b>  with
  static member ofList (xs:list<'k*'v>) =
      let res = new Dictionary<_,_> ()
      for k, v in xs do
          res.Add (k, v)
      res    

module Dictionary = 
   let ofList xs = Dictionary<_,_>.ofList xs

然后你摆脱警告。

Dictionary.ofList ["1",1;"2",2];;
val it : Dictionary<string,int> = dict [("1", 1); ("2", 2)]