Golang:将结构体转换为偏移量为0的嵌入式结构体。

huangapple go评论84阅读模式
英文:

Golang: convert struct to embedded at offset 0 struct

问题

我有一些不同的结构体,比如Big,其中包含在偏移量0处嵌入的Small结构体。如何在不知道Big类型的代码中访问Small的结构字段,但已知Small位于偏移量0处?

type Small struct {
    val int
}

type Big struct {
    Small
    bigval int
}

var v interface{} = Big{}
// 这里我只知道'Small'结构体,并且我知道它位于变量的开头
v.(Small).val // 编译错误

看起来编译器在理论上能够处理这样的表达式,因为它知道Big类型在偏移量0处嵌入了Small类型。有没有办法做这样的事情(也许使用unsafe.Pointer)?

英文:

I have some different structs like Big with Small embedded at offset 0.
How can I access Small's structure fields from code, that doesn't know anything about Big type, but it is known that Small is at offset 0?

type Small struct {
	val int
}

type Big struct {
	Small
	bigval int
}

var v interface{} = Big{}
// here i only know about 'Small' struct and i know that it is at the begining of variable
v.(Small).val // compile error

It seems that compiler is theoretically able to operate such expression, because it knows that Big type has Small type embedded at offset 0. Is there any way to do such things (maybe with unsafe.Pointer)?

答案1

得分: 1

避免在可能的情况下使用unsafe。可以使用反射(reflect包)来完成上述任务:

var v interface{} = Big{Small{1}, 2}

rf := reflect.ValueOf(v)
s := rf.FieldByName("Small").Interface()

fmt.Printf("%#v\n", s)
fmt.Printf("%#v\n", s.(Small).val)

输出结果(在Go Playground上尝试):

main.Small{val:1}
1

注意:

这适用于任何字段,不仅限于第一个字段(在“偏移量0”处)。这也适用于命名字段,而不仅仅是嵌入字段。但对于未导出的字段,这种方法不起作用。

英文:

Avoid using unsafe whenever possible. The above task can be done using reflection (reflect package):

var v interface{} = Big{Small{1}, 2}

rf := reflect.ValueOf(v)
s := rf.FieldByName("Small").Interface()

fmt.Printf("%#v\n", s)
fmt.Printf("%#v\n", s.(Small).val)

Output (try it on the Go Playground):

main.Small{val:1}
1

Notes:

This works for any field, not just the first one (at "offset 0"). This also works for named fields too, not just for embedded fields. This doesn't work for unexported fields though.

答案2

得分: 1

以下是您提供的代码的翻译:

type Small struct {
    val int
}

type Big struct {
    Small
    bigval int
}

func main() {
    var v = Big{Small{10}, 200}
    print(v.val)
}

这段代码定义了两个结构体类型:SmallBigSmall结构体包含一个整数字段val,而Big结构体嵌入了Small结构体,并且还有一个整数字段bigval

main函数中,创建了一个Big类型的变量v,并初始化为Big{Small{10}, 200}。然后,打印出vval字段的值为10。

英文:
type Small struct {
    val int
}

type Big struct {
    Small
    bigval int
}

func main() {
    var v = Big{Small{10},200}
    print(v.val)
}

答案3

得分: 1

虽然使用反射可以实现,但它会带来性能损失,并且在Go语言中不是惯用的方式。

我认为你应该使用接口。像这样:

package main

import (
	"fmt"
)

type MySmall interface {
	SmallVal() int
}

type Small struct {
	val int
}

func (v Small) SmallVal() int {
	return v.val
}

type Big struct {
	Small
	bigval int
}

func main() {
	var v interface{} = Big{Small{val: 3}, 4}
	fmt.Printf("Small val: %v", v.(MySmall).SmallVal())
}

输出:

Small val: 3
英文:

While answer with reflection is working but it has performance penalties and is not idiomatic to Go.

I believe you should use interface. Like this

https://play.golang.org/p/OG1MPHjDlQ

package main

import (
	"fmt"
)

type MySmall interface {
	SmallVal() int
}

type Small struct {
	val int
}

func (v Small) SmallVal() int {
	return v.val
}

type Big struct {
	Small
	bigval int
}

func main() {
	var v interface{} = Big{Small{val: 3}, 4}
	fmt.Printf("Small val: %v", v.(MySmall).SmallVal())
}

Output:

Small val: 3

huangapple
  • 本文由 发表于 2016年8月25日 18:40:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/39143091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定