Golang中间接创建新类型结构体或调用接口函数的方法有哪些

Golang中间接创建新类型结构体或调用接口函数的方法有哪些 我最近在修改一些Go语言代码时,想出了这个switch/case语句:

        switch what {
        case "Type1"  : names[name] = new(Type1)
        case "Type2" : names[name] = new(Type2)
        case "Type3": names[name] = new(Type3)
        }

根据what的值,我想创建一个新的结构体类型实例。如果不使用switch/case语句或等效的if/else语句,如何实现这一点?我在想,what可以作为哈希表的键,而类型的类型可以作为值。但是,Go语言中new(*间接类型*)的语法是什么?

类似地,假设我们想在其中一个新结构体类型上调用n个接口函数之一,这也可以通过switch/case语句完成,如下所示:

type TypeStar interface {
    Func1()
    Func2()
}

type Type1 struct {}

func (a *Type1) Func() { fmt.Printf ("func1,type1") }
func (a *Type1) Func2() { fmt.Printf ("func2,type1") }

type Type2 struct {}

func (a *Type2) Func1() { fmt.Printf ("func1,type2") }
func (a *Type2) Func2() { fmt.Printf ("func2,type2") }

type Type3 struct {}

func (a *Type3) Func1() { fmt.Printf ("func1,type3") }
func (a *Type3) Func2() { fmt.Printf ("func2,type3") }
...
        if object, ok := names[name]; ok {
                switch what {
                case "func1"  : object.func1()
                case "func2" : object.func2()
                }
        }

但是如何修改这段代码以消除switch/case语句,并使用例如哈希表代替?也就是说,Go语言中将间接接口函数存储为哈希表值的语法是什么,以便可以在哈希表中查找接口函数然后调用它?


更多关于Golang中间接创建新类型结构体或调用接口函数的方法有哪些的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

谢谢,@skillian!这说得通。

抱歉,在你回复的时候我扩展了原来的问题。如果你也能就问题的第二部分提供更多见解,那就太好了。谢谢!

更多关于Golang中间接创建新类型结构体或调用接口函数的方法有哪些的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


对于第二部分,我认为你需要使用反射包。你可以这样做(为简洁起见,省略了错误处理):

v := reflect.ValueOf(obj1)
f := v.MethodByName("Func1")
res := f.Call(nil)

感谢 @skillian

你的所有建议都非常有效。

关于第一个建议,起初我有些困惑,以为 “newOf()” 是一个常规的 Go 内置函数。但后来我理解你的意思是在某种注册表中预先创建所需的类型,然后从注册表中“复制”所需的类型。这似乎效果很好。希望我的理解是正确的 🙂

关于第二个建议,它完全可行。不过我在想,switch / case 语句最终会不会比 .MethodByName() 函数更快?我想我可以通过分析不同数量的 switch case 来找出答案。

再次感谢你的帮助!

反射会比 switch/case 语句慢。对于 newOf 的歧义表示抱歉,但我想你现在应该明白了。为了更清楚,以下是我设想中类似的做法:

var typeMap = map[string]func() interface{} {
    "type1": func() interface{} { return new(Type1) },
    "type2": func() interface{} { return new(Type2) },
    "type3": func() interface{} { return new(Type3) },
}

func newOf(name string) interface{} {
    return typeMap[name]()
}

感谢 @skillian 的澄清和代码。

我采用了一种略有不同的方式,想知道我的不同做法是否存在某些缺陷:

typeRegistry := map[string]TypeStar {
    "Type1" : new(Type1) ,
    "Type2" : new(Type2) }
names[name] = typeRegistry[what]

它似乎可以工作,但缺点是需要预先实例化类型(对内存稍差?)?然后复制它们理论上可能比按需创建更慢?简而言之,比起我的代码,我更喜欢你那个使用函数的、更清晰的代码 🙂

你目前采用的方法(或者使用映射、if…else 等)是从字符串转换到类型的唯一途径。你可以使用 reflect 包来“参数化”类型,但首先必须拥有对该类型的引用。例如,给定以下代码:

type MyType struct{}

func main() {
    x := newOf("MyType")
}

newOf 能够工作的唯一方式是,你拥有一个将名称映射到类型的注册表。例如,database/sql 驱动程序包就是这样工作的:之所以需要导入驱动程序包(通常使用下划线 _ 导入),是为了确保它们被注册到某个全局的 database/sql 驱动程序注册表中,这通常通过一个 init 函数来实现。

在Go语言中,可以通过映射(map)结合反射(reflect)或函数类型来实现间接创建结构体实例和调用接口函数。以下是两种情况的解决方案:

1. 间接创建结构体实例

使用map[string]reflect.Type存储类型信息,然后通过反射创建实例:

import "reflect"

var typeRegistry = map[string]reflect.Type{
    "Type1": reflect.TypeOf(Type1{}),
    "Type2": reflect.TypeOf(Type2{}),
    "Type3": reflect.TypeOf(Type3{}),
}

func createInstance(typeName string) interface{} {
    if t, ok := typeRegistry[typeName]; ok {
        return reflect.New(t).Interface()
    }
    return nil
}

// 使用示例
names := make(map[string]interface{})
what := "Type1"
name := "instance1"

if instance := createInstance(what); instance != nil {
    names[name] = instance
}

2. 间接调用接口函数

使用map[string]func(interface{})存储函数映射,通过类型断言调用具体方法:

type TypeStar interface {
    Func1()
    Func2()
}

// 方法映射表
var funcRegistry = map[string]func(TypeStar){
    "func1": func(obj TypeStar) { obj.Func1() },
    "func2": func(obj TypeStar) { obj.Func2() },
}

// 使用示例
if object, ok := names[name].(TypeStar); ok {
    if fn, exists := funcRegistry[what]; exists {
        fn(object)
    }
}

或者使用反射调用方法:

import "reflect"

var methodRegistry = map[string]string{
    "func1": "Func1",
    "func2": "Func2",
}

// 使用反射调用
if object, ok := names[name]; ok {
    if methodName, exists := methodRegistry[what]; exists {
        reflect.ValueOf(object).MethodByName(methodName).Call(nil)
    }
}

完整示例

package main

import (
    "fmt"
    "reflect"
)

type TypeStar interface {
    Func1()
    Func2()
}

type Type1 struct{}
func (a *Type1) Func1() { fmt.Println("func1,type1") }
func (a *Type1) Func2() { fmt.Println("func2,type1") }

type Type2 struct{}
func (a *Type2) Func1() { fmt.Println("func1,type2") }
func (a *Type2) Func2() { fmt.Println("func2,type2") }

var typeRegistry = map[string]reflect.Type{
    "Type1": reflect.TypeOf(Type1{}),
    "Type2": reflect.TypeOf(Type2{}),
}

var funcRegistry = map[string]func(TypeStar){
    "func1": func(obj TypeStar) { obj.Func1() },
    "func2": func(obj TypeStar) { obj.Func2() },
}

func main() {
    names := make(map[string]interface{})
    
    // 创建实例
    what := "Type1"
    name := "obj1"
    if t, ok := typeRegistry[what]; ok {
        names[name] = reflect.New(t).Interface()
    }
    
    // 调用方法
    if obj, ok := names[name].(TypeStar); ok {
        funcRegistry["func1"](obj)
        funcRegistry["func2"](obj)
    }
}

这些方法消除了switch/case语句,通过映射表实现动态类型创建和方法调用。反射方案更灵活但性能较低,接口方案类型安全且性能更好。

回到顶部