【问题标题】:f# member constraints and IEnumerable collectionsf# 成员约束和 IEnumerable 集合
【发布时间】:2013-12-04 12:38:59
【问题描述】:

我是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 并且具有强类型Item-member 的东西作为上面然后调用Seq.cast 与来自Item-member 的相应类型。

这是我的第一次摸索:

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

这几乎可以工作,但不适用于真实类型!

> 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'
>

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

有什么帮助吗?

编辑 2:这确实有效!

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

【问题讨论】:

  • 我不太确定你是如何从不想要 Seq.cast 到静态发现类型参数的——我想我错过了你最终想要在这里做的事情。您可以扩展 ListView 类型以包含将返回强类型序列的 ToEnumerable() 方法:seq { for item in this.Items -&gt; item };这将摆脱演员表。
  • Collection 类型可以被理解为 seq { } 表达式,这让我觉得这是一个小漏洞,因为 for 可以识别类型,但不能直接用作序列。
  • 哦,酷!我不知道seq { for item in listView.Items -&gt; item } 实际上会正确推断类型。我想知道这背后是否有什么魔力?
  • @johv - F# 的for 支持非IEnumerable&lt;_&gt; 值实际上有两种不同的风格,正如规范在6.5.6 部分中所解释的那样。基本上,编译器会寻找一个GetEnumerator() 方法,它的返回类型支持MoveNext : unit -&gt; bool 和Current : 't 成员。如果't 不是obj,那么它会使用它;否则,它会查看是否还有一个具有更具体类型的 Item 索引属性,并将动态转换插入该类型。

标签: generics f# ienumerable inline type-inference


【解决方案1】:

听起来你想在these lines 中添加一些东西,在这种情况下你的代码几乎是正确的。

请注意,一个问题是您正试图将索引属性与方法互换使用,但这样做并不正确:对于名为 Item 的索引属性,方法的实际名称将是 get_Item (或者,您可以使用语法member Item : int -&gt; 'a with get 而不是直接使用get_Item 方法)。

【讨论】:

  • 是的,有效!我确实想使用 cast,只是使用 Item 来获取或绑定类型。
猜你喜欢
  • 2020-12-20
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 2017-12-18
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多