4 回答

TA貢獻1794條經驗 獲得超8個贊
這是不可能的。原因之一是傳遞參數的機制因函數而異,使用interface{}
arg 并不意味著“接受任何東西”。例如,一個以結構為參數的函數將接收該結構的每個成員,但一個以包含該結構的 interface{} 的函數將接收兩個字,一個包含結構的類型,另一個包含指向它。
因此,在不使用泛型的情況下,實現這一點的唯一方法是使用適配器函數。

TA貢獻2011條經驗 獲得超2個贊
使用反射包來處理具有任意參數和結果類型的函數。
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 也失去了類型安全性。匿名包裝函數增加了冗長性。

TA貢獻1784條經驗 獲得超2個贊
作為記錄,隨著 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明確的。
沒有命名約束的示例替代方案:
// accept and return T
decorate[T any](inner func(T) T, arg T) T
// only return T
decorate[T any](inner func() T) T
// return T and error
decorate[T any](inner func(T) (T, error), arg T) (T, error)
// N-ary function
decorate[T, U any](inner func(T, U) (T, error), argt T, argu U) (T, error)
明顯的限制是接口約束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")
}
游樂場:https ://go.dev/play/p/3q01NiiWsve

TA貢獻1818條經驗 獲得超7個贊
有沒有辦法將 func(string) string 類型的現有函數轉換為 func(interface{}) interface{} 類型,以便它也可以傳遞給裝飾器函數,而無需將其包裝在新的匿名函數中(見 funcB)?
不,就這么簡單:不。
- 4 回答
- 0 關注
- 179 瀏覽
添加回答
舉報