golang 如何访问提升的类型
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
?
你不需要反省。一种方法是使用接口:
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, theMap.Len())
for _,k:=range theMap {
cf=append(cf,k.GetCommon())
}
return cf
}
但你还必须做:
appleMap := make(map[string]WithCommon)
orangeMap := make(map[string]WithCommon)
请参阅 Burak 的回答,该回答在必须调用方法来接收值方面做出了合理的妥协。
无论如何,下面是一个按照您的计划使用反射的解决方案。请注意,common
必须是 Common
(导出字段),否则反射包无法读取它。
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)
}
}
如果您知道自己在做什么,可以使用 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() // 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
}
我在两个特定结构中提升了一个 '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
?
你不需要反省。一种方法是使用接口:
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, theMap.Len())
for _,k:=range theMap {
cf=append(cf,k.GetCommon())
}
return cf
}
但你还必须做:
appleMap := make(map[string]WithCommon)
orangeMap := make(map[string]WithCommon)
请参阅 Burak 的回答,该回答在必须调用方法来接收值方面做出了合理的妥协。
无论如何,下面是一个按照您的计划使用反射的解决方案。请注意,common
必须是 Common
(导出字段),否则反射包无法读取它。
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)
}
}
如果您知道自己在做什么,可以使用 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() // 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
}