我正在嘗試擴展我對 Go 函式指標的了解,我有一個問題,即在 Go 中將函式作為引數傳遞什么是可能的,什么是不可能的。
假設我想撰寫一個decorator()可以包裝任何現有函式的函式。為簡單起見,讓我們將其限制為只接受一個引數并只回傳一個值的函式。
如果我撰寫了一個接受func(interface{}) interface{}作為引數的裝飾器,只要我傳入的函式也接受/回傳一個interface{}型別,它就會隱式地作業(請參閱 參考資料funcA)。
我的問題是 —— 有沒有辦法將現有的 type 函式轉換為 of 型別func(string) string,func(interface{}) interface{}以便它也可以傳遞到裝飾器函式中,而無需將其包裝在新的匿名函式中(請參閱 參考資料funcB)?
package main
import (
"fmt"
)
func decorate(inner func(interface{}) interface{}, args interface{}) interface {} {
fmt.Println("Before inner")
result := inner(args)
fmt.Println("After inner")
return result
}
func funcA(arg interface{}) interface{} {
fmt.Print("Inside A, with arg: ")
fmt.Println(arg)
return "This is A's return value"
}
func funcB(arg string) string {
fmt.Print("Inside B, with arg: ")
fmt.Println(arg)
return "This is B's return value"
}
func main() {
// This one works. Output is:
//
// Before inner
// Inside A, with arg: (This is A's argument)
// After inner
// This is A's return value
//
fmt.Println(decorate(funcA, "(This is A's argument)"))
// This doesn't work. But can it?
//fmt.Println(decorate(funcB, "(This is B's argument)"))
}
uj5u.com熱心網友回復:
這不可能。原因之一是傳遞引數的機制因函式而異,并且使用interface{}arg 并不意味著“接受任何東西”。例如,將結構體作為 arg 的函式將接收該結構體的每個成員,但采用包含該結構體的 interface{} 的函式將接收兩個字,一個包含結構的型別,另一個包含指向它。
因此,在不使用泛型的情況下,實作這一點的唯一方法是使用配接器函式。
uj5u.com熱心網友回復:
使用反射包來處理具有任意引數和結果型別的函式。
func decorate(inner interface{}, args interface{}) interface{} {
fmt.Println("Before inner")
result := reflect.ValueOf(inner).Call([]reflect.Value{reflect.ValueOf(args)})
fmt.Println("After inner")
return result[0].Interface()
}
在操場上運行代碼。
與decorate問題中的函式一樣,此答案中的函式假定一個引數和一個結果。必須修改該函式以處理其他函式型別。
OP 應考慮問題中提出的匿名包裝函式與此處使用反射包之間的權衡。通過反射 API 呼叫函式比通過匿名包裝器呼叫函式慢。反射 API 也失去了型別安全性。匿名包裝函式增加了冗長。
uj5u.com熱心網友回復:
根據記錄,隨著 Go 1.18 和泛型的引入,該decorator功能幾乎變得微不足道。
您可以這樣宣告型別約束:
type UnaryFunc[T any] interface {
func(T) T
}
約束本身被引數化T以允許采用和回傳任意型別的一元函式。
decorate然后在函式中使用型別引數實體化約束。簽名變成:
decorate[T any, F UnaryFunc[T]](inner F, arg T) T
由于型別推斷,你可以通過具體的函式的自變數,都T和F將是明確的。
明顯的限制是介面約束UnaryFunc僅指定接受和回傳恰好一個型別 arg 的函式T。你不能這樣做,因為介面約束的型別集可能包括支持相同操作的型別——并且用一個 arg 呼叫與用 N 個 args 呼叫不兼容。
完整的程式:
package main
import (
"fmt"
)
type UnaryFunc[T any] interface {
func(T) T
}
func decorate[T any, F UnaryFunc[T]](inner F, arg T) T {
fmt.Println("before inner")
result := inner(arg)
fmt.Println("after inner")
return result
}
func funcA(arg int) int {
fmt.Println("inside A with:", arg)
return arg
}
func funcB(arg string) string {
fmt.Println("inside B with:", arg)
return arg
}
func main() {
// this works
decorate(funcA, 200)
// this also works
decorate(funcB, "Func B")
}
不幸的是,Go2 游樂場已經過時了。要運行上面的程式,您必須使用gotip.
uj5u.com熱心網友回復:
有沒有辦法將 func(string) string 型別的現有函式轉換為 func(interface{}) interface{} 型別,以便它也可以傳遞到裝飾器函式中,而無需將其包裝在新的匿名函式中(見funcB)?
不。就這么簡單:不。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/343554.html
上一篇:為什么我在Go中遇到分段違規錯誤
