如何将结构体作为参数传递给函数?

27

我该如何在Golang中将结构体作为参数传递给函数?以下是我的代码:

package main

import (
    "fmt"
)

type MyClass struct {
    Name string
}

func test(class interface{}) {
    fmt.Println(class.Name)
}

func main() {

    test(MyClass{Name: "Jhon"})
}

当我运行它时,出现了这样的错误。

# command-line-arguments
/tmp/sandbox290239038/main.go:12: class.Name undefined (type interface {} has no field or method Name)

这里有play.golang.org的fiddle地址。

5个回答

48

你正在寻找;

func test(class MyClass) {
    fmt.Println(class.Name)
}
目前这种方法将class识别为实现空接口的某个对象(意味着在该范围内其字段和方法完全未知),这就是为什么会出现错误的原因。
你的另一个选择是像这样:
func test(class interface{}) {
     if c, ok := class.(MyClass); ok { // type assert on it    
         fmt.Println(c.Name)
     }
}

但是在你的例子中没有必要这样做。只有当你需要进行类型判断或者根据 class 实际类型执行不同的代码路径时,这种方式才有意义。


我尝试了你的第二个结构体代码片段,但是当函数接受interface{}时,我无法简单地传递我的结构体,那么我该如何为函数传递正确的参数? - Sir
1
我们能否在不使用断言的情况下完成这个任务?因为它接收动态结构。 - Angger
@Angger,如果你知道接口提供了哪些方法,你就不必进行类型断言,只需调用其中一个即可。如果传入的类型提供的方法集不同,并且你需要根据传入的类型执行特定操作,则需要使用类型断言。 - evanmcdonnal
TIL 你可以从类型断言中获取第二个参数。感谢这个:D - Meghan

20

根据您的需求,您至少有两个选项:

  1. 在结构体类型上使用方法
  2. 以结构体类型为参数的函数
package main

import "fmt"

type MyClass struct {
    Name string
}

func main() {
    cls := MyClass{Name: "Jhon"}

    // Both calls below produce same result
    cls.StructMethod()  // "Jhon"
    FuncPassStruct(cls) // "Jhon"
}

// Method on struct type
func (class MyClass) StructMethod() {
    fmt.Println(class.Name)
}

// Function that takes struct type as the parameter
func FuncPassStruct(class MyClass) {
    fmt.Println(class.Name)
}

我相信其他人可能会提供一些我忘记的界面技巧。


现在我想添加一个接口,用于保存所有函数/方法的列表。我们该如何实现? - Amulya Kashyap
@AmulyaKashyap,你能详细说明一下吗?描述一下你的目标是什么。 - openwonk

3
如果您真的想以通用的方式发送任何结构,就像在您的原始问题中所写的那样(空接口参数),以便可以访问AnyStruct.SomeField和AnotherStruct.SomeField或AnyOtherStruct.AnyField,据我所知,go语言的反射功能是解决此类问题的方法。
例如,如果您查看JSON marshal函数,它接受几乎任何结构作为“v”参数发送到一个带有空接口参数的函数中。然后,该marshal函数最终会调用:
e.reflectValue(reflect.ValueOf(v), opts)

但是,你可能正在寻找像其他答案建议的那样简单的东西,不需要高级反射(一种通用的编程方式)。

然而,我发布这个答案,以防其他读者对于通常情况下无需事先知道结构字段就可以发送结构体的方法感兴趣。

JSON解析器之所以需要以通用方式访问结构体,是为了方便用户定义任何想要的结构体,然后Go自动地将其映射到JSON语法。你可以想象有数百种不同的结构布局,你只想把它们放在一个文本文件中,而不需要事先知道所有结构布局 - 函数可以在运行时弄清楚,这就是反射工作的方式(其他语言称其为运行时类型信息、元编程等)。


1
请查看 DebugReceive 方法。它使用了结构体用户,及其方法。 https://play.golang.org/p/E_WkLWGdeB
package main

import "fmt"

// user defines a user in the program.
type user struct {
    name   string
    email  string
    status string
}

func debug(u user) {
    fmt.Printf("%+v\n", u)
}

// notify implements a method with a value receiver.
func (u user) notify() {
    fmt.Printf("User: Sending User Email To %s<%s>\n", u.name, u.email)
}

// changeEmail implements a method with a pointer receiver.
func (u *user) changeEmail(email string) {
    u.email = email
}

func (u *user) changeName(name string) {
    u.name = name
}

func (u *user) Send() {
    u.notify()
}

// main is the entry point for the application.
func main() {
    // Pointers of type user can also be used to methods
    // declared with a value receiver.
    john := &user{"John", "john@exemple.com", "enabled"}

    john.changeName("John Smith")
    john.changeEmail("john@gmail.com")
    john.notify()

    Receive(john)
    debug(user{"Willy", "Willy@exemple.com", "enabled"})
}

func Receive(user interface {
    changeName(s string)
    changeEmail(s string)
    Send()
}) {
    user.changeName("Bill")
    user.changeEmail("bill@billy-exemple.com")
    user.Send()
}



0
您需要使用.()语法将参数从接口转换为特定的结构体。
package main

import (
    "fmt"
)

type MyClass struct {
    Name string
}

func test(class interface{}) {
    classStr := class.(MyClass)
    fmt.Println(classStr.Name)
}

func main() {

    test(MyClass{Name: "Jhon"})
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接