按其值将任何结构字段名称转换为字符串
Posted
技术标签:
【中文标题】按其值将任何结构字段名称转换为字符串【英文标题】:Convert any struct field name to string by it's value 【发布时间】:2022-01-15 08:31:03 【问题描述】:在 GO 中,我想创建类似 C++ 风格的枚举:ClassName::EnumName::EnumValue。
struct MyClass
enum class EnumName Success, Error ;
;
GO变体:
package MyPackage
type enumValue struct val int
type knownValus struct
Success, Error enumValue
var EnumName = knownValus
Success: enumValue0,
Error: enumValue1,
我的 C++ 类中有很多枚举,保留这个枚举名称对我来说非常重要。当我输入枚举名称时,我想查看此特定枚举的所有可能已知值,以便能够选择正确的值。 还有一个好处:我们可以将这个枚举传递给一个函数:
func HandleSmth(v enumValue)
MyPackage.HandleSmth(MyPackage.EnumName.Success)
这太不可思议了!我将无法使用其他数据类型调用我的函数!
那么 Enum 的风格是这样的:
const (
Success = iota
Error = iota
)
这很丑,因为我无法确定我的函数可以处理的正确值。
问题是:如何实现通用的 EnumToString 函数,让我可以将任何枚举从私有包转换为字符串?
我已经为特定类型的结构实现了这个,但我不能为一般...
package EnumToString
func Convert(result enumValue) string
possibleEnums := EnumName
elems := reflect.ValueOf(&possibleEnums).Elem()
if elems.NumField() == 0
panic("No fields found")
GetUnexportedField := func(field reflect.Value) interface
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
typeOfT := elems.Type()
for i := 0; i < elems.NumField(); i++
field := elems.Field(i)
valStruct := GetUnexportedField(field).(enumValue)
val := GetUnexportedField(reflect.ValueOf(&valStruct).Elem().Field(0))
switch val.(type)
case int:
if val.(int) == GetUnexportedField(reflect.ValueOf(&result).Elem().Field(0)).(int)
return typeOfT.Field(i).Name
return ""
fmt.printLn(EnumToString.Convert(MyPackage.EnumName.Success)) // Should print 'Success'
fmt.printLn(EnumToString.Convert(OtherPackage.OtherName.OtherVale)) // Should print 'OtherValue'
但我的方法只适用于一个特定的结构。
如何使它与任何结构一起工作?
【问题讨论】:
它不会打印字段名称“成功”或“错误” 正如我最近提到的,我不想使用这种语法,你读过我的问题吗? 【参考方案1】:也许你可以尝试类似 go enums 风格的东西,像这样:
type WeekdayEnum int
const (
Sunday WeekdayEnum = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
参考书籍:“Go 编程语言”- Alan A. A. Donovan 和 Brian W. Kernighan
【讨论】:
【参考方案2】:您可以通过定义仅用于该枚举的特殊数据类型来强制函数仅采用 Go 中的某个枚举类型。
与 C++ 不同,Go 没有枚举,只有常量,并且没有打印枚举值名称的方法,但是为给定的枚举添加相对简单。
不幸的是,像 C++ 一样,Go 不支持像 Java 那样将值限制为仅有效的枚举值(尽管如果你愿意,当然可以添加一个检查)。
你不应该尝试使用反射来尝试添加语言功能或模仿C++,这会让你不开心,最好只是接受Go在这方面的限制并使用你所拥有的语言。
所以要定义一个数据类型,你只需添加一个以 int 作为基本类型的新类型
// Special datatype based on int - you can also add functions to this
type Foo int
然后定义您的枚举值。如果使用 iota ,它只分配给第一个 0 值(而不是像上面一样)。如果您想分配明确的值,您可以这样做(为了清楚地显示长列表或特定的连续值,如错误代码)。在 Go 中假设零值是 None 或 Invalid 而不是 Success 会更惯用。
// Values for Foo datatype
const (
FooInvalid = iota
FooError
FooSuccess
)
然后使用它们:
// Accepts one param which must be a Foo
func HandleFoo(f Foo) string
return fmt.Sprintf("%s",f)
如果你想把它作为一个字符串,你必须编写或生成你自己的函数
// String representation of Foo
func (f Foo) String() string
switch f
case FooSuccess:
return "Foo is Good"
case FooError:
return "Foo failed"
default:
return "Invalid"
如果正确性非常重要,您还可以编写一个函数来强制检查 Foo 是否为有效值(Go 不提供此功能,我认为 C++ 也不提供此功能?)。
// Is this foo valid?
func (f Foo) Valid() bool
return f == FooSuccess || f == FooError
这是操场上的一个例子: https://go.dev/play/p/4qjUoIIIIhU
【讨论】:
以上是关于按其值将任何结构字段名称转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章
如何从另一个输入文本字段Javascript中输入的值将值设置为输入隐藏字段