简体   繁体   中英

Map of methods in Go

I have several methods that I'm calling for some cases (like Add, Delete, etc..). However over time the number of cases is increasing and my switch-case is getting longer. So I thought I'd create a map of methods, likeGo map of functions ; here the mapping of functions is trivial. However, is it possible to create a map of methods in Go?

When we have a method:

func (f *Foo) Add(a string, b int) { }

The syntax below create compile-time error:

actions := map[string]func(a, b){
        "add": f.Add(a,b),
}

Is it possible to create a map of methods in Go?

Yes. Currently:

actions := map[string]func(a string, b int){
        "add": func(a string, b int) { f.Add(a, b) },
}

Later: see the go11func document guelfi mentioned.

There is currently no way to store both receiver and method in a single value (unless you store it in a struct). This is currently worked on and it may change with Go 1.1 (see http://golang.org/s/go11func ).

You may, however, assign a method to a function value (without a receiver) and pass the receiver to the value later:

package main

import "fmt"

type Foo struct {
    n int
}

func (f *Foo) Bar(m int) int {
    return f.n + m
}

func main() {
    foo := &Foo{2}
    f := (*Foo).Bar
    fmt.Printf("%T\n", f)
    fmt.Println(f(foo, 42))
}

This value can be stored in a map like anything else.

You can do this using Method Expressions:

https://golang.org/ref/spec#Method_expressions

However, this makes the function take the receiver as a first argument:

actions := map[string]func(Foo, string, int){
  "add": Foo.Add
}

Similarly, you can get a function with the signature func(*Foo, string, int) using (*Foo).Add

If you want to use pointer to type Foo as receiver, like in:

func (f *Foo) Add(a string, b int) { }

then you can map string to function of (*Foo, string, int), like this:

var operations = map[string]func(*Foo, string, int){
    "add": (*Foo).Add,
    "delete": (*Foo).Delete,
}

Then you would use it as:

var foo Foo = ...
var op string = GetOp() // "add", "delete", ...
operations[op](&foo, a, b)

where GetOp() returns an operation as string, for example from a user input. a and b are your string and int arguments to methods.

This assumes that all methods have the same signatures. They can also have return value(s), again of the same type(s).

It is also possible to do this with Foo as receiver instead of *Foo . In that case we don't have to de-reference it in the map, and we pass foo instead of &foo .

I met with a similar question.

How can this be done today, 9 years later:

the thing is that the receiver must be passed to the method map as the first argument. Which is pretty unusual.

package main

import (
    "fmt"
    "log"
)

type mType struct {
    str string
}

func (m *mType) getStr(s string) {
    fmt.Println(s)
    fmt.Println(m.str)
}

var (
    testmap = make(map[string]func(m *mType, s string))
)

func main() {
    test := &mType{
        str: "Internal string",
    }
    testmap["GetSTR"] = (*mType).getStr
    method, ok := testmap["GetSTR"]
    if !ok {
        log.Fatal("something goes wrong")
    }
    method(test, "External string")
}

https://go.dev/play/p/yy3aR_kMzHP

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM