在Go中,可以像这样创建函数类型(https://golang.org/ref/spec#Function_types)
type Printer func(s string)
如何找到满足此类型的所有功能?例如,如果我有下面的文件,我怎么能发现consolePrinter
是Printer
?
package main
import "fmt"
func main() {
printToScreen(consolePrinter)
}
// Printer defines a function that prints a string.
type Printer func(s string)
func printToScreen(p Printer) {
p("Hello")
}
// consolePrinter is a Printer (the function signature is identical).
func consolePrinter(s string) {
fmt.Println(s)
}
我尝试了大师,但implements
功能似乎不支持功能类型。
guru implements ./main.go:#134
/Users/adrian/go/gurutest/main.go:10.6-10.12: signature type Printer implements only interface{}
答案 0 :(得分:3)
您正在寻找assignable到Printer
的功能。
guru
工具不支持“可分配给”查询。
您可以使用go/types程序包编写程序以查找可分配给Printer
的函数。按照tutorial键入检查代码,然后使用AssignableTo查找功能。
如果您只想在gofmted代码中查找在包级别声明的函数,那么使用regexp搜索代码可能就足够了。这种方法不准确,但在许多编辑器或命令行中都很简单。
答案 1 :(得分:0)
此处consolePrinter
不属于Printer
类型,需要将consolePrinter
声明为Printer
类型。您看到console.Printer
是一个具有相同功能的函数基础类型,它可以传递到printToScreen
的{{1}}。Printer type
。看看他们的类型的差异
package main
import (
"fmt"
"reflect"
)
func main() {
printToScreen(consolePrinter)
fmt.Println(reflect.TypeOf(consolePrinter)) // Prints func(string) as its type
}
// Printer defines a function that prints a string.
type Printer func(s string)
func printToScreen(p Printer) {
var consoleprint Printer // declare to be of type Printer
fmt.Println(reflect.TypeOf(consoleprint)) // Prints main.Printer
p("Hello")
}
// consolePrinter is a Printer (the function signature is identical).
func consolePrinter(s string) {
fmt.Println(s)
}
答案 2 :(得分:0)
工作示例,基于Thundercat的回答。 guru
需要执行类似的操作,以便为查找要传入的合适函数提供支持(例如http.HandleFunc
的实现)。
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
)
const hello = `package main
import "fmt"
const x = 1;
func main() {
fmt.Println("Hello, world")
}
// Printer defines a function that prints a string.
type Printer func(s string)
func consolePrinter(s string) {
fmt.Println(s)
}
`
func main() {
fset := token.NewFileSet()
// Parse the input string, []byte, or io.Reader,
// recording position information in fset.
// ParseFile returns an *ast.File, a syntax tree.
f, err := parser.ParseFile(fset, "hello.go", hello, 0)
if err != nil {
log.Fatal(err) // parse error
}
// A Config controls various options of the type checker.
// The defaults work fine except for one setting:
// we must specify how to deal with imports.
conf := types.Config{Importer: importer.Default()}
// Type-check the package containing only file f.
// Check returns a *types.Package.
pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil)
if err != nil {
log.Fatal(err) // type error
}
names, signatures := getFunctionTypes(pkg)
for i, name := range names {
fmt.Println("Functions which implement", name)
for _, implementor := range getFunctionsWhichImplement(signatures[i], pkg) {
fmt.Println(implementor)
}
}
}
func getFunctionTypes(pkg *types.Package) (names []string, signatures []*types.Signature) {
for _, name := range pkg.Scope().Names() {
o := pkg.Scope().Lookup(name)
if _, isType := o.(*types.TypeName); !isType {
continue
}
var sig *types.Signature
var isFunc bool
if sig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
continue
}
signatures = append(signatures, sig)
names = append(names, name)
}
return
}
func getFunctionsWhichImplement(sig *types.Signature, pkg *types.Package) (fns []types.Object) {
for _, name := range pkg.Scope().Names() {
o := pkg.Scope().Lookup(name)
if _, isType := o.(*types.TypeName); isType {
continue
}
var csig *types.Signature
var isFunc bool
if csig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
continue
}
if types.AssignableTo(sig, csig) {
fns = append(fns, o)
}
}
return
}
此代码的输出如下所示:
Functions which implement Printer
func cmd/hello.consolePrinter(s string)
答案 3 :(得分:-2)
我确信有更好的方法来处理特定的打印情况,但总的来说,采用更加惯用的方法来使用界面可能会更好。
声明实现接口的接口和类型:
type StringPrinter interface {
PrintString(string)
}
type Console struct {
// console specific stuff
}
func (c *Console) PrintString(s string) {
// Printing code
}
type Paper struct {
// paper specific stuff
}
func (p *Paper) PrintString(s string) {
// Printing code
}
然后以不同的方式打印,您可以通过以下通用方式访问界面:
func main() {
var sp StringPrinter
sp = &Console{ /* member inits */ }
sp.PrintString("Hello on console")
sp = &Paper{ /* member inits */ }
sp.PrintString("Hello on paper")
}
您将能够在这种代码形式上使用guru来查找实现StringPrinter接口的对象。