f#成员约束和IEnumerable集合

时间:2013-12-04 12:38:59

标签: generics f# ienumerable inline type-inference

我是F#的新手,今天我看到了F#的成员约束功能。

我在考虑是否可以(ab)以下列方式使用它......

假设我想迭代获取ListView中项目的文本。我可以这样做:

let listView = new System.Windows.Forms.ListView()
let itemTexts = 
    listView.Items
    |> Seq.cast<System.Windows.Forms.ListViewItem>
    |> Seq.map (fun item -> item.Text)

上面的Seq.cast不是类型安全且有点难看。

我在想的是:

> let item (i:int) = listView.Items.Item i;;

val item : int -> System.Windows.Forms.ListViewItem

所以IEnumerable的类型实际上是以循环方式编码到类中的。

我看到的成员约束的例子如下:

type Cat() = 
    member this.Walk() = printfn "cat walk"
type Dog() = 
    member this.Walk() = printfn "dog walk"

let adapter() = 
    let cat = Cat()
    let dog = Dog()
    let inline walk (x : ^T) = (^T : (member Walk : unit->unit) (x))
    walk(cat)
    walk(dog)

我认为它与我上面想做的有点类似。

这可以用来创建一个像下面这样使用的函数吗?

let itemTexts = 
    listView.Items
    |> Seq.asSeq
    |> Seq.map (fun item -> item.Text)

修改:我们的想法是实现一个通用Seq.asSeq扩展方法,该方法采用实现非泛型IEnumerable并具有强类型{{{ 1}} - 上面的成员,然后使用Item成员中的相应类型调用Seq.cast

这是我的第一次摸索尝试:

Item

这几乎可行,但不是真正的类型!

let listView = new System.Windows.Forms.ListView()
let items = 
    listView.Items

type cat() = 
    member this.Item (i:int) = "cat walk"
    interface System.Collections.IEnumerable with
       member this.GetEnumerator() = ((seq { yield (this.Item 0) }) :> System.Collections.IEnumerable).GetEnumerator()

type dog() = 
    member this.Item (i:int) = 6
    interface System.Collections.IEnumerable with
       member this.GetEnumerator() = ((seq { yield (this.Item 0) }) :> System.Collections.IEnumerable).GetEnumerator()

let cat = cat()
let dog = dog()

let inline asSeq (x : ^t) =
    let dummy _ = (^t : (member Item : int -> 'a) (x, 0))
    Seq.cast<'a> x

我想这是我的假类型没有正确反映.NET索引属性......

任何帮助?

编辑2:这实际上有效!

> cat;;
val it : cat = seq ["cat walk"]
> asSeq cat;;
val it : seq<string> = seq ["cat walk"]
> asSeq dog;;
val it : seq<int> = seq [6]
> asSeq items;;

  asSeq items;;
  ------^^^^^

C:\...\stdin(5,7): error FS0001: The type 'System.Windows.Forms.ListView.ListViewItemCollection' does not support any operators named 'Item'
>

1 个答案:

答案 0 :(得分:3)

听起来你想要these lines之类的东西,在这种情况下你的代码几乎是正确的。

请注意,一个问题是您尝试与方法交替使用索引属性,但是您没有做到这一点:对于名为Item的索引属性,方法的实际名称将为{{ 1}}(或者,您可以使用语法get_Item而不是直接使用member Item : int -> 'a with get方法)。