Golang中结构体与接口类型的解析与应用

Golang中结构体与接口类型的解析与应用 你好,

我在某个问题上有点卡住了,这个问题在网上看起来是合乎逻辑的,但我在家尝试时却无法让它正常工作。我创建了一个结构体,并将其传递给一个方法:

user := grinch.User{
Username: “lucas”,
}
p := reflect.ValueOf(&user) // 注意:获取 x 的地址。
fmt.Println(“type of p:”, p.Type())
fmt.Println(“settability of p:”, p.CanSet())
v := p.Elem()
fmt.Println(“settability of v:”, v.CanSet())
grinchDb.Where(&user)

当我检查类型时,它是 *grinch.User,到目前为止一切正常。但是当我在 Where 方法中检查时,类型的值变成了 *interface {},我大概能猜到原因,但当我寻找关于这个主题的帮助时,到处都写着 Go 应该记住原始类型并因此显示 *grinch.User,但它没有这样做。

也许我的代码哪里出错了,这是 Where 方法:

func (g *Grinch) Where(obj interface{}) {
p := reflect.ValueOf(&obj) // 注意:获取 x 的地址。
fmt.Println(“type of p:”, p.Type())
fmt.Println(“settability of p:”, p.CanSet())
v := p.Elem()
fmt.Println(“settability of v:”, v.CanSet())
}

有人能看出我这里做错了什么吗?


更多关于Golang中结构体与接口类型的解析与应用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

非常感谢,我为此困扰了好几个小时,却没有注意到那个小小的 & 符号。

更多关于Golang中结构体与接口类型的解析与应用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@lucas_tambarin

请移除你 (*Grinch).Where 函数中 p := reflect.ValueOf(&obj) 里的 & 符号。这是在获取 obj 参数的地址,而不是你传入的值。

问题出在 Where 方法中对反射的使用。当你传递 &userWhere 方法时,参数 obj 已经是接口类型,而 &obj 获取的是这个接口变量的地址,而不是原始结构体的地址。

以下是修正后的代码:

// 调用方
user := grinch.User{
    Username: "lucas",
}
grinchDb.Where(&user)

// Where 方法
func (g *Grinch) Where(obj interface{}) {
    // 直接使用 obj 进行反射,不需要再取地址
    p := reflect.ValueOf(obj)
    fmt.Println("type of p:", p.Type())
    fmt.Println("settability of p:", p.CanSet())
    
    // 如果需要修改值,需要获取指针指向的元素
    if p.Kind() == reflect.Ptr {
        v := p.Elem()
        fmt.Println("type of v:", v.Type())
        fmt.Println("settability of v:", v.CanSet())
    }
}

输出会是:

type of p: *grinch.User
settability of p: false
type of v: grinch.User
settability of v: true

关键点:

  1. obj 已经是接口类型,包含了指向 *grinch.User 的指针
  2. reflect.ValueOf(&obj) 获取的是接口变量本身的地址,类型为 *interface {}
  3. 应该直接使用 reflect.ValueOf(obj) 来获取接口中存储的值

如果需要修改结构体字段,可以这样操作:

func (g *Grinch) Where(obj interface{}) {
    v := reflect.ValueOf(obj)
    if v.Kind() == reflect.Ptr && !v.IsNil() {
        elem := v.Elem()
        if elem.Kind() == reflect.Struct {
            // 可以修改结构体字段
            if field := elem.FieldByName("Username"); field.IsValid() && field.CanSet() {
                if field.Kind() == reflect.String {
                    field.SetString("modified")
                }
            }
        }
    }
}
回到顶部