我的问题与此问题有些相关 - Functions with generic parameter types - 但我无法理解如何做我想做的事。
我想定义一个'后代函数来包含对各种C#类的'Descendants'的调用,如下所示:
让后代名称(xDocument:XDocument)= xDocument.Descendants name
让后代命名(xElement:XElement)= xElement.Descendants name
这种方法不起作用,因为我们有'后代'的重复定义。
我认为可以使用内联函数和静态解析参数来定义以下方法来代替:
let inline descendants name (xml : ^x when ^x : (member Descendants : XName -> seq<XElement>)) =
xml.Descendants name
但是在尝试这样做时我遇到了这个错误:
根据此程序点之前的信息查找不确定类型的对象。在此程序点之前可能需要类型注释来约束对象的类型。这可以允许解析查找。
有没有办法可以写第二个功能来做我想做的事?
答案 0 :(得分:14)
一般来说,我认为像^x
这样的帽子类型可能会被过多使用(至少,从SO的问题数量来判断)。它是一个强大的功能,但它的设计主要是为了解决通用算术问题。我认为他们可以使F#程序不必要地复杂化。
如果您只使用XDocument
和XElement
,那么答案非常简单,因为您可以使用XContainer
这是他们的公共基类并具有{{1方法:
Descendants
如果找不到公共基类,那么你当然可以使用let descendants name (xml:XContainer) = xml.Descendants(name)
// Both of these will work fine
descendants (XName.Get "foo") xd
descendants (XName.Get "foo") xe
类型,但你也可以使用普通的重载,这在F#中是可行的,但只适用于对象类型的成员:
^a
(因为你引用了一个问题,答案已经表明重载与成员合作,这对你来说可能不是什么新鲜事。但是对于将来会发现这个问题的其他人可能会有用。) / p>
答案 1 :(得分:4)
下面的代码编译(并提示调用静态成员约束函数所需的语法)。
open System.Xml.Linq
let descendants1 name (xDocument:XDocument) = xDocument.Descendants name
let descendants2 name (xElement:XElement) = xElement.Descendants name
let inline descendants name (xml : ^x when ^x : (member Descendants : XName -> seq<XElement>)) =
(^x : (member Descendants : XName -> seq<XElement>) (xml,name))
let xd = XDocument.Load("http://www.somexml.com")
let ds = descendants (XName.op_Implicit "foo") xd
let xe = XElement.Load("http://www.somexml.com")
let eds = descendants (XName.op_Implicit "foo") xe