go - 函数调用的 Go 类型
问题描述
关键字 likego
和defer
expect 将函数调用作为参数。是否有可以以相同方式使用的类型?(例如,编写一个期望函数调用的函数 - 与函数相反 - 作为参数)。
解决方案
不,那里没有。你不能对你的函数做同样的事情。
go
并defer
受到语言规范的支持,并且规则由编译器强制执行。
您可以做的是使用函数类型的变量/值,您可以稍后/随时调用它,就好像它是一个函数一样。
例如:
func myFunc() {
fmt.Println("hi")
}
func main() {
var f func()
f = myFunc
f() // This calls the function value stored in f: myFunc in this example
}
编辑:要拥有您在评论中提到的功能:只需将函数调用及其参数包装在 a 中func()
,然后使用 / 传递它。
例如:
func launch(f func()) {
fmt.Println("Before launch")
go func() {
defer fmt.Println("After completion")
f()
}()
}
使用它:
func main() {
launch(func() {
fmt.Println("Hello, playground")
})
time.Sleep(time.Second)
}
哪些输出(在Go Playground上尝试):
Before launch
Hello, playground
After completion
是的,这不是一个确切的解决方法。如果参数可能更改,您必须在调用之前制作它们的副本launch()
,并在函数文字(闭包)中使用副本,如下例所示:
s := "Hello, playground"
s2 := s // make a copy
launch(func() {
fmt.Println(s2) // Use the copy
})
s = "changed"
模拟自动参数保存
对于具体的函数类型,我们可以构造一个帮助函数,它为我们提供自动参数保存。此辅助函数必须具有相同的签名,并且返回一个不带参数的函数。返回的函数是一个闭包,它使用参数调用原始函数。调用这个辅助函数的行为是保存参数的机制,所以用法和defer
.
例如,助手fmt.Println(s)
是:
func wrapPrintln(s string) func() {
return func() {
fmt.Println(s)
}
}
并使用它:
launch(wrapPrintln(s))
int
具有 2 个参数的函数示例:
func Sum(a, b int) {
fmt.Println("Sum:", a+b)
}
func WrapSum(a, b int) func() {
return func() {
Sum(a, b)
}
}
launch(WrapSum(a, b))
上面WrapPrintln()
包裹WrapSum()
了一个具体的函数,它不能用于其他函数(被包裹的函数是“连线的”)。我们也可以将包装函数作为参数:
func WrapFuncIntInt(f func(a, b int), a, b int) func() {
return func() {
f(a, b)
}
}
我们可以这样使用它:
launch(WrapFuncIntInt(Sum, a, b))
在Go Playground上试试这个。
使用反射来避免手动复制
您可以使用反射来避免手动复制,但在这个解决方案中,我们实际上并没有调用函数,只是传递它。同样由于使用反射,它会变慢。另一个优点是这种“感觉”是通用的(我们可以使用具有不同签名的函数),但是我们失去了编译时的安全性。
func launch(f interface{}, params ...interface{}) {
fmt.Println("Before launch")
go func() {
defer fmt.Println("After completion")
pv := make([]reflect.Value, len(params))
for i, v := range params {
pv[i] = reflect.ValueOf(v)
}
reflect.ValueOf(f).Call(pv)
}()
}
调用它的示例:
func main() {
i, s := 1, "Hello, playground"
launch(fmt.Printf, "%d %q\n", i, s)
i, s = 2, "changed"
time.Sleep(time.Second)
}
哪些输出(在Go Playground上尝试):
Before launch
1 "Hello, playground"
After completion
您可以利用自动参数保存的单一例外
我们可以使用一个例外。这是方法值。如果x
具有静态类型T
并且T
的方法集包含该方法M
,我们可以使用x.M
(不调用它)。
表达式x.M
是一个方法值,它保存了一个副本,x
当调用表达式的结果(它是一个函数值)时,该副本将用作接收者。
例子:
type myParams struct {
format string
i int
s string
}
func (mp myParams) Call() {
fmt.Printf(mp.format, mp.i, mp.s)
}
func main() {
p := myParams{format: "%d %q\n", i: 1, s: "Hello, playground"}
launch(p.Call) // p is saved here
p.i, p.s = 2, "changed"
time.Sleep(time.Second)
}
func launch(f func()) {
fmt.Println("Before launch")
go func() {
defer fmt.Println("After completion")
f()
}()
}
它输出相同。在Go Playground上尝试一下。
推荐阅读
- ruby-on-rails - Rails 自动加载 has_one 关系
- android - Android Studio - 自定义 ListView 崩溃应用
- html - 如何使用引导程序 4 将背景图像添加到行
- c# - 带有类参数的 WPF 绑定标签内容
- user-interface - 可以在 Flutter 中将文本刻入按钮
- eclipse - Eclipse CDT 将头文件中的外部“C”标记为语法错误
- javascript - 如何跟踪用户在组件上停留的时间
- javascript - 在 Python 中使用 Splice 移动函数
- django - Django 日期验证,需要帮助
- php - imagecreatefromjpeg():gd-jpeg:JPEG 库报告不可恢复的错误: