英文:
golang how to access promoted type
问题
我有一个在两个特定结构体中推广的“common”结构体。例如:
type common struct {
name string
}
type apple struct {
common
}
type orange struct {
common
}
省略了与apple
和orange
特定的细节。
我有一个类型特定的映射,例如map[string]*apple
和map[string]*orange
。
我试图编写一个可以提取common
指针的单个函数。从我目前尝试的情况来看,似乎需要使用反射。
我的函数是:
func getFruitArray(theMap interface{}) []*common {
m := reflect.ValueOf(theMap)
cf := make([]*common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk)
cf = append(cf, v.Interface().(*common))
}
return cf
}
这个函数在cf = append(cf, v.Interface().(*common))
处失败,报错为:
panic: interface conversion: interface {} is *main.apple, not *main.common
在这个函数中,有没有一种方法可以访问推广的结构体common
,而不需要明确引用apple
或orange
?
英文:
I have a 'common' structure promoted within two specific structures. For example:
type common struct {
name string
}
type apple struct {
common
}
type orange struct {
common
}
Details specific to apple
and orange
are omitted.
I have a type-specific map of each, e.g., map[string]*apple
and map[string]*orange
.
I am trying to make a single function that can extract the common
pointers. From what I've tried so far, reflection appears required.
My function is:
func getFruitArray(theMap interface{}) []*common {
m := reflect.ValueOf(theMap)
cf := make([]*common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk)
cf = append(cf, v.Interface().(*common))
}
return cf
}
This function fails at cf = append(cf, v.Interface().(*common))
with:
panic: interface conversion: interface {} is *main.apple, not *main.common
Is there a way to access the promoted struct common
without specifically referencing apple
or orange
in this function?
答案1
得分: 4
请稍等,我会为您翻译这段代码。
英文:
See Burak's answer which makes the reasonable compromise of having to call a method to receive the value.
Regardless, below is a solution which uses reflection as you planned. Note that common
needs to be Common
(exported field) else the reflect package cannot read it.
package main
import (
"log"
"reflect"
)
type Common struct {
name string
}
type apple struct {
Common
}
type orange struct {
Common
}
func getFruitArray(theMap interface{}) []*Common {
m := reflect.ValueOf(theMap)
cf := make([]*Common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk)
f := v.Elem().FieldByName("Common")
cf = append(cf, f.Addr().Interface().(*Common))
}
return cf
}
func main() {
appleMap := make(map[string]*apple)
orangeMap := make(map[string]*orange)
a1 := &apple{}
a1.name = "my apple"
appleMap["test"] = a1
o1 := &orange{}
o1.name = "my orange"
orangeMap["test2"] = o1
f1 := getFruitArray(appleMap)
for _, c := range f1 {
log.Printf("f1: %s", c.name)
}
f2 := getFruitArray(orangeMap)
for _, c := range f2 {
log.Printf("f2: %s", c.name)
}
}
答案2
得分: 2
你不需要使用反射。一种方法是使用接口:
type common struct {
name string
tag string
}
func (c *common) GetCommon() *common {return c}
type WithCommon interface {
GetCommon() *common
}
然后你可以这样做:
func getFruitArray(theMap map[string]WithCommon) []*common {
cf := make([]*common, 0, len(theMap))
for _, k := range theMap {
cf = append(cf, k.GetCommon())
}
return cf
}
但你还需要做以下操作:
appleMap := make(map[string]WithCommon)
orangeMap := make(map[string]WithCommon)
英文:
You don't need reflection. One way is to use an interface:
type common struct {
name string
tag string
}
func (c *common) GetCommon() *common {return c}
type WithCommon interface {
GetCommon() *common
}
Then you can do:
func getFruitArray(theMap map[string]WithCommon) []*common {
cf := make([]*common, 0, theMap.Len())
for _,k:=range theMap {
cf=append(cf,k.GetCommon())
}
return cf
}
But you also have to do:
appleMap := make(map[string]WithCommon)
orangeMap := make(map[string]WithCommon)
答案3
得分: 1
如果你知道自己在做什么,可以使用unsafe
包。但如果你不知道,请不要使用。
func getFruitArray(theMap interface{}) []*common {
m := reflect.ValueOf(theMap)
cf := make([]*common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk).Elem() // 使用elem解引用指针
t := v.Type()
// 如果你知道common总是第一个字段
// 那么你可以直接使用v.Field(0)。但如果common的位置不确定,就像下面的循环一样。
for i := 0; i < v.NumField(); i++ {
sf := t.Field(i)
if sf.Anonymous && sf.Name == "common" {
f := v.Field(i)
// 1. 获取common字段的地址
// 2. 首先将其转换为unsafe.Pointer
// 3. 然后将其转换为*common
c := (*common)(unsafe.Pointer(f.UnsafeAddr()))
cf = append(cf, c)
}
}
}
return cf
}
https://go.dev/play/p/XMi86jj2wiW
英文:
If you know what you are doing you could use the unsafe
package. But if you don't then don't.
func getFruitArray(theMap interface{}) []*common {
m := reflect.ValueOf(theMap)
cf := make([]*common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk).Elem() // use elem to dereference the pointer
t := v.Type()
// If you know that common is always the first field
// then you can just use v.Field(0). But if common's
// position is not guaranteed then use a loop like below.
for i := 0; i < v.NumField(); i++ {
sf := t.Field(i)
if sf.Anonymous && sf.Name == "common" {
f := v.Field(i)
// 1. get the address of the common field
// 2. convert it first to unsafe.Pointer
// 3. then convert it to *common
c := (*common)(unsafe.Pointer(f.UnsafeAddr()))
cf = append(cf, c)
}
}
}
return cf
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论