【问题标题】:Can I polymorphically access static methods?我可以多态访问静态方法吗?
【发布时间】:2017-09-23 20:22:23
【问题描述】:

我需要调用类构造函数before 的静态成员。该类实现了一个接口,稍后我将需要在after the object is constructed 上调用(多态)相同的静态成员。

我相信有些语言允许使用点之前的实例名称访问静态方法,例如

myClassInstance.staticMethod

F# 似乎不允许这样做,特别是如果类继承自接口,因为接口不能包含静态方法。

下面的代码举例说明了这个问题:

module Types

type IMult =
    abstract member Something : float
    abstract member MultBy : float -> float

open Types

type Point(x: float) =
    interface Types.IMult with
        member this.Something with get() = x
        member this.MultBy (x: float) =
            (this :> IMult).Something * x

    static member public foo x = x + 3.0

let myPointConstructorArgument = Point.foo 5.0
let pt = Point(myPointConstructorArgument)

printfn "%f" <| (pt :> IMult).MultBy 10.0 // OK: 80.0

let bar (instnc: IMult) = instnc.foo // Error: foo not defined 
let pt0 = Point(bar pt 6.0) // No good: error above

我的问题是:是否有可能以某种方式检索对象的类,然后调用抽象方法?

我尝试了以下方法:

let ptType = pt0.GetType()
let mtd = ptType.foo // Error: foo not defined
let mtd = ptType.GetMethod("foo") // Ok, but not what I need, see below
let i2 = mtd 3.0 // Error: mtd is not a function

对替代方法有什么建议吗?

【问题讨论】:

  • 你可以将它定义为扩展方法,但是为什么你需要用实例调用它呢?它似乎不受实例本身的影响。
  • 查看静态解析的类型约束。
  • @Gustavo - 我不需要用实例调用它。实际上它不依赖于实例(因为它是静态的)。我想用实例来调用它,因为我不能用类名来调用它,因为在调用时,类可以是几种选择中的任何一种。如果 F# 让我从实例中调用它,问题将得到解决。

标签: static f# polymorphism


【解决方案1】:

听起来您真正要寻找的是“类型类”或“见证”,这是一种尚不支持的语言功能,但是跟踪此功能的实现的this issue 相当活跃。
目前在 F# 中,有几种方法可以获得所需的多态访问。

1) 通过接口实例化成员。似乎您不能将多态成员直接放在您的 IMult 类型上,因为您需要在构造实例之前访问它,但是您可以为“类型”创建一个接口,然后通过作为一个附加参数(这与类型类“在幕后”的工作方式非常相似):

//your "trait"
type IFoo<'a> =
  abstract member Foo : float -> float

//your "witness", defines what "Foo" means for the "Point" type.  Other witnesses could define what "Foo" means for other types.
let fooablePoint = { new IFoo<Point> with member this.Foo x = Point.foo x }

let bar (fooable:IFoo<'a>) = fooable.Foo //this isn't really necessary anymore, but illustrates that a function can use a polymorphic "static" function, given a witness
let pt0 = Point(bar fooablePoint 6.0)

2) 静态成员约束(如上面 cmets 中所指出的):

let inline bar (pt:^a) x = 
  (^a : (static member foo : float -> float) x)

一方面,这使您能够多态地访问类型上的静态成员,尽管它不应该被过度使用according to fsharp.org。它的语法非常不透明,很难破译。此外,它仅适用于内联函数,因此如果无处不在,可能会严重破坏应用程序的性能。请注意,这也仅在您已经拥有 ^a 类型的实例时才有效,因此此解决方案也可能不适合您。

3) 只需将 foo 函数传递给其他需要它的函数即可:

let bar (foo:float -> float) x = foo x // type annotations wouldn't be necessary, and just provided for clarity
let pt0 = Point (bar Point.foo 6.0)

4)(在下面的 cmets 中讨论):在 IMult 内部你仍然可以定义

abstract member foo: float -> float 

那么在Point内,就有了

interface Types.IMult with 
  member this.Foo x = Point.foo x

这可能会给你你想要的一切。然后,该成员将作为Point 类型上的静态成员,IMult 上的实例成员存在,因此它不再是真正的“静态多态性”,但它可能会实现您的目标。

【讨论】:

  • 这有点过头了,但我看过1和3(我会看2,后面看起来更复杂)。在这两种情况下,您都在代码中使用了“Point”一词。但是在运行时我不知道我正在使用什么类,它可能是 Point 或另一个继承自 IMult 的类。所以这两个建议(1和3)似乎并没有解决问题。我错过了什么吗?
  • @Soldalma,是的,您可能希望它是多态的。您需要将多态函数/接口定义在与pt 变量相同的级别。因此,对于选项 #1,您可以定义,例如let fooable : IFoo&lt;'a&gt; = fooablePointpt 同时出现,它们将一起出现(您可以将 fooablept 一起或代替 bar 传递给 bar)。对于选项#2,您将在与pt 相同的位置定义let foo : float -&gt; float = Point.foo,它也将与pt 并排。如果没有类型类,就无法将其与pt“捆绑”。
  • @Soldalma,请记住,您仍然可以在IMult 中定义abstract member foo: float -&gt; float,在Point 中,只需使用interface Types.IMult with member this.Foo x = Point.foo x,这可能会给您想要的一切。然后,该成员将作为Point 类型上的静态成员和IMult 上的实例成员存在,但它不再是真正的“静态多态性”了。
  • 我最终遵循了最后一个建议。即在接口中添加一个抽象方法,并将静态方法复制到其中。这行得通,虽然它不是很优雅,因为我有两个功能可以做同样的事情。无论如何,如果您在答案中添加了该替代方案,对读者可能会有所帮助。我无法使其他建议发挥作用。为了具有多态性,我必须传递一个实现接口的参数。但是该对象不能用于检索静态方法,因为接口对此一无所知。
【解决方案2】:

我会选择上面的“1”,不要纠结于类型类——它们不存在,而且在某些方面,它们并不理想。

对我来说,您缺少抽象,将其视为某种工厂或域实体,它捕获您的静态方法,并简单地明确传递它。

type MultDomain<'a when 'a :> IMult> =
    abstract member foo :  float -> float

type PointDomain =
    interface MultDomain<Point> with
        member o.foo x = x + 3.0

(约束“'a :> IMult”在您的上下文中不是必需的,但表明您可以使用您尚未在此接口中创建的对象的类型)

所以答案是 “是的,只要你将它们映射到某种类型的实例方法,你可以多态地调用”......或者也许 “不,但您可以将它们映射到可以多态调用的某种类型的实例方法”。

(我个人从来没有静态方法,甚至避免使用构造函数!)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多