Go语言如何将struct字段作为map键实现DB枚举映射及相关操作
首先先修正你示例代码里的语法问题,给出基础的映射定义参考:
type Message struct { SomeKey string SomeOtherKey string } // 映射key直接用结构体公开字段的字符串名即可,方便后续反射操作 var MessageToDBEnum = map[string]string{ "SomeKey": "some_key", "SomeOtherKey": "some_other_key", }
问题1:动态使用map的key对struct字段取值/赋值的实现
可以通过Go标准库的reflect反射包实现,示例代码如下:
import "reflect" // 读取结构体字段值,兼容值类型和指针类型的Message入参 func GetMessageField(msg interface{}, fieldName string) (string, bool) { val := reflect.ValueOf(msg) // 如果入参是指针,先取指向的实际值 if val.Kind() == reflect.Ptr { val = val.Elem() } // 校验字段是否存在、是否是公开可访问的字段 fieldVal := val.FieldByName(fieldName) if !fieldVal.IsValid() || !fieldVal.CanInterface() || fieldVal.Kind() != reflect.String { return "", false } return fieldVal.String(), true } // 给结构体字段赋值,入参必须是Message指针才能修改原对象 func SetMessageField(msg *Message, fieldName string, value string) bool { val := reflect.ValueOf(msg).Elem() fieldVal := val.FieldByName(fieldName) // 校验字段是否存在、是否可修改 if !fieldVal.IsValid() || !fieldVal.CanSet() || fieldVal.Kind() != reflect.String { return false } fieldVal.SetString(value) return true }
遍历map的场景下,直接取出map的key作为fieldName参数传入上述方法即可完成对应字段的读写操作。
如果对性能要求极高,不想承担反射的开销,也可以用预定义操作函数的方案,不需要反射:
type FieldOperator struct { DBColumnName string Get func(Message) string Set func(*Message, string) } var MessageFieldOpMap = map[string]FieldOperator{ "SomeKey": { DBColumnName: "some_key", Get: func(m Message) string { return m.SomeKey }, Set: func(m *Message, v string) { m.SomeKey = v }, }, "SomeOtherKey": { DBColumnName: "some_other_key", Get: func(m Message) string { return m.SomeOtherKey }, Set: func(m *Message, v string) { m.SomeOtherKey = v }, }, }
问题2:保证映射覆盖所有公开字段的方案
最常用、成本最低的方案是通过单元测试做运行时校验,示例单测代码如下:
import "testing" import "reflect" func TestMessageToDBEnumIsComplete(t *testing.T) { msgType := reflect.TypeOf(Message{}) // 遍历Message结构体的所有字段 for i := 0; i < msgType.NumField(); i++ { field := msgType.Field(i) // 跳过非公开的字段(首字母小写) if field.PkgPath != "" { continue } // 检查公开字段是否存在于映射中 if _, exists := MessageToDBEnum[field.Name]; !exists { t.Errorf("公开字段%s未加入MessageToDBEnum映射", field.Name) } } }
只要在CI流程中执行该单测,就能第一时间发现漏加的字段。
Go原生不支持编译期的映射完备性检查,如果一定要在编译期发现问题,可以自定义字段枚举类型,将所有字段定义为常量后再构造映射,但实现成本远高于单元测试方案,一般不推荐。
内容的提问来源于stack exchange,提问作者GeriTol
相关产品推荐
相关产品推荐

