将任何结构体字段名转换为其值的字符串。

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

Convert any struct field name to string by it's value

问题

在Go语言中,你可以使用类似C++风格的方式创建枚举。下面是你提供的代码的翻译:

package MyPackage

type enumValue struct { val int }

type knownValues struct {
    Success, Error enumValue
}

var EnumName = knownValues {
    Success: enumValue{0},
    Error:   enumValue{1},
}

对于你的第一个问题,你可以使用反射来实现一个通用的EnumToString函数。下面是你提供的代码的翻译:

package EnumToString

import (
    "fmt"
    "reflect"
    "unsafe"
)

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 ""
}

func main() {
    fmt.Println(EnumToString.Convert(MyPackage.EnumName.Success)) // 应该打印 'Success'
    fmt.Println(EnumToString.Convert(OtherPackage.OtherName.OtherValue)) // 应该打印 'OtherValue'
}

希望这可以帮助到你!如果你有任何其他问题,请随时问我。

英文:

In GO I want to create enum like in C++ style: ClassName::EnumName::EnumValue.

struct MyClass {
    enum class EnumName { Success, Error };
};

GO variant:

package MyPackage

type enumValue struct { val int }


type knownValus struct {
	Success, Error enumValue
}

var EnumName = knownValus {
	Success: enumValue{0},
	Error:   enumValue{1},
}

I have a lot of enums in my C++ class it is very important for me to keep this enum name. When I type enum name I want to see all the possible known values for this specific enum to be able to choose the proper one.
One more advantage: we can pass this enum to a function:

func HandleSmth(v enumValue) {}
MyPackage.HandleSmth(MyPackage.EnumName.Success)

This is incredible! I will not be able to call my function with a different data type!

And what about Enum in style like this:

const (
	Success = iota
	Error = iota
)

It's pretty ugly because I cannot figure out the proper values that my function can handle.

> The question is: how to implement general EnumToString function that will allow me to convert any enum from a private packages to a string?

I've implemented this for a specific type struct, but I cannot for a general...

package EnumToString
func Convert(result enumValue) string {
	possibleEnums := EnumName
	elems := reflect.ValueOf(&amp;possibleEnums).Elem()
	if elems.NumField() == 0 {
		panic(&quot;No fields found&quot;)
	}

	GetUnexportedField := func(field reflect.Value) interface{} {
		return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
	}
	typeOfT := elems.Type()

	for i := 0; i &lt; elems.NumField(); i++ {
		field := elems.Field(i)
		valStruct := GetUnexportedField(field).(enumValue)
		val := GetUnexportedField(reflect.ValueOf(&amp;valStruct).Elem().Field(0))

		switch val.(type) {
		case int:
			if val.(int) == GetUnexportedField(reflect.ValueOf(&amp;result).Elem().Field(0)).(int) {
				return typeOfT.Field(i).Name
			}
		}
	}

	return &quot;&quot;
}

fmt.printLn(EnumToString.Convert(MyPackage.EnumName.Success)) // Should print &#39;Success&#39;
fmt.printLn(EnumToString.Convert(OtherPackage.OtherName.OtherVale)) // Should print &#39;OtherValue&#39;

But my approach works for the only one specific struct.

> How to make it working with any structs?

答案1

得分: 1

也许你可以尝试类似Go语言的枚举风格,像这样:

type WeekdayEnum int

const (
  Sunday WeekdayEnum = iota
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
)

书籍参考:《The Go Programing Language》- Alan A. A. Donovan & Brian W. Kernighan

英文:

Maybe you can try something like go enums style, like this:

type WeekdayEnum int

const (
  Sunday WeekdayEnum = iota
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
)

Book reference: "The Go Programing Language" - Alan A. A. Donovan & Brian W. Kernighan

答案2

得分: 1

你可以通过定义一个专门用于该枚举的特殊数据类型来强制限制Go中只接受特定枚举类型的函数。

与C++不同,Go没有枚举,只有常量,并且没有一种方法可以打印枚举值的名称,但是对于给定的枚举,这相对简单。

与Java一样,不幸的是,Go不支持限制值仅为有效枚举值的功能(尽管如果需要,你当然可以添加检查)。

你不应该尝试使用反射来添加语言特性或模仿C++,这会让你不开心,最好只接受Go在这方面的限制并与你所拥有的语言一起工作。

因此,要定义一个数据类型,你只需添加一个以int为基本类型的新类型

// 基于int的特殊数据类型 - 你还可以向其添加函数
type Foo int

然后定义枚举值。如果使用iota,它只会分配给第一个0值(而不是上面两个都分配)。如果你想分配显式值,可以这样做(对于长列表或特定连续值,如错误代码,这样更清晰)。在Go中,更符合惯例的做法是假设零值为None或Invalid,而不是Success。

// Foo数据类型的值
const (
    FooInvalid = iota
	FooError
    FooSuccess
)

然后使用它们:

// 接受一个参数,该参数必须是Foo类型
func HandleFoo(f Foo) string {
	return fmt.Sprintf("%s", f)
}

然后,如果你想将其作为字符串使用,你需要编写或生成自己的函数

// 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++也没有)。

// 这个Foo是否有效?
func (f Foo) Valid() bool {
	return f == FooSuccess || f == FooError
}

这里是一个在playground上的示例:
https://go.dev/play/p/4qjUoIIIIhU

英文:

You can enforce a function taking only a certain enum type in Go by defining a special datatype used only for that enum.

Unlike C++ Go does not have enums, only constants, and does not have a way to print an enum value name, but this is relatively simple to add for a given enum.

Like C++ Go unfortunately does not support limiting the values to only valid enum values as Java does (though you can of course add a check for that if you wish).

You should not attempt to use reflect to attempt to add language features or imitate C++, this will make you unhappy, better to just accept the limitations of Go in this regard and work with the language you have.

So to define a datatype you'd just add a new type with int as the base type

// Special datatype based on int - you can also add functions to this
type Foo int

Then define your enum values. If using iota is it only assigned to the first 0 value (not both as you have it above). If you want to assign explicit values you can do so (for clarity in long lists or specific contiguous values like error codes). It would be more idiomatic in Go to assume the zero value is None or Invalid rather than Success.

// Values for Foo datatype
const (
    FooInvalid = iota
	FooError
    FooSuccess
)

Then use them:

// Accepts one param which must be a Foo
func HandleFoo(f Foo) string {
	return fmt.Sprintf(&quot;%s&quot;,f)
}

Then if you want this as a string you'd have to write or generate your own function

// String representation of Foo
func (f Foo) String() string {
	switch f {
	case FooSuccess:
		return &quot;Foo is Good&quot;
	case FooError:
		return &quot;Foo failed&quot;
	default:
		return &quot;Invalid&quot;
	}
}

If correctness is very important, you could also write a function to enforce validity which checks the Foo is a valid value (Go doesn't provide this, and I don't think C++ does either?).

// Is this foo valid?
func (f Foo) Valid() bool {
	return f == FooSuccess || f == FooError
}

Here is an example on the playground:
https://go.dev/play/p/4qjUoIIIIhU

huangapple
  • 本文由 发表于 2021年12月11日 01:04:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/70308050.html
匿名

发表评论

匿名网友

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

确定