2017-09-23 93 views
1

我需要調用類的一個靜態成員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 

有什麼建議?

+0

您可以將其定義爲一個擴展方法,但爲什麼你需要用實例來調用它呢?它似乎不受實例本身的影響。 – Gustavo

+1

查看靜態解析的類型約束。 –

+0

@Gustavo - 我不需要用實例調用它。事實上它不依賴於實例(因爲它是靜態的)。我想用實例調用它,因爲我不能用類名稱來調用它,因爲在調用的時候,類可以是幾種選擇中的任何一種。如果F#讓我從實例中調用它,則問題將得到解決。 – Soldalma

回答

2

這聽起來像你真正需要的是「類型班」或「目擊者」,這是一種語言功能,目前還不支持,但追蹤此功能實施的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)靜止構件約束(如在以上註釋)中指出:在一方面

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)(在下面的評論中討論):內IMult你仍然可以定義

abstract member foo: float -> float 

然後內Point,只是有

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

這可能會給y ou你想要的一切。那麼該成員將作爲Point類型的一個靜態成員存在,IMult上的一個實例成員,因此它不再是真正的「靜態多態」,但它可能實現您的目標。

+0

這有點凌駕於我的頭上,但我已經看過1和3(我會看2,後面看起來更復雜)。在這兩種情況下,你都在代碼中使用了「Point」這個詞。但是在運行時我不知道我使用的是什麼類,它可能是Point或從IMult繼承的另一個類。所以這兩個建議(1和3)似乎不能解決問題。我錯過了什麼嗎? – Soldalma

+1

@Soldalma,是的,你可能希望它多態性更高。您需要將多態函數/接口定義在與您的'pt'變量相同的級別上。因此,對於選項#1,您可以定義,例如'let fooable:IFoo <'a> = fooablePoint'與'pt'同時,他們會一起走過(你可以將'fooable'與'pt'一起或代替'pt'傳遞給'bar')。對於選項#2,您可以在與'pt'相同的位置定義'let foo:float - > float = Point.foo',它也會與'pt'一起使用。沒有類型類的'pt'沒有辦法「捆綁它」。 –

+1

@Soldalma,請記住,您仍然可以在'IMult'內部定義'abstract member foo:float - > float',並且在'Point'內部,使用'interface Types.IMult'成員this.Foo x = Point.foo x ',這可能會給你想要的一切。然後該成員將存在_both_作爲'Point'類型的靜態成員,以及'IMult'上的實例成員,但它不再是真正的「靜態多態」。 –

0

我會用上面的「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」是不是在你的情況下必需的,但表明你可以使用你還沒有在此界面中創建對象的類型)

所以答案是 「是的,只要您將它們映射到可以多態調用的某種類型的實例方法」...或者也許 「否,但您可以將它們映射到某種類型的實例方法,您可以以多態方式調用」。

(我個人從來沒有靜態方法,甚至避免建設者!)

相關問題