How to get all constants of a type in Go

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

How to get all constants of a type in Go

问题

这是一个示例:

package main

type State int

const (
    Created State = iota
    Modified
    Deleted
)

func main() {
    // 这里有一些代码,我需要获取该类型的所有可用常量列表。
}

这个用例是为了创建一个有限状态机(FSM)。能够获取所有常量将有助于编写一个测试用例,确保每个新值在FSM映射中都有相应的条目。

英文:

Here is an example:

package main

type State int

const (
    Created State = iota
    Modified
    Deleted
)

func main() {
    // Some code here where I need the list
    // of all available constants of this type.
}

The use case for this is to create a Finite State Machine (FSM). Being able to get all constants will help me in writing a test case that will ensure that every new value has a corresponding entry in the FSM map.

答案1

得分: 12

如果你的常量都是按顺序排列的,你可以使用以下代码:

type T int

const (
	TA T = iota
	TB
	TC
	NumT
)

func AllTs() []T {
	ts := make([]T, NumT)
	for i := 0; i < int(NumT); i++ {
		ts[i] = T(i)
	}
	return ts
}

你也可以将输出缓存到init()函数中。这只适用于所有常量都按顺序使用iota进行初始化的情况。如果你需要适用于所有情况的解决方案,可以使用显式切片。

英文:

If your constants are all in an order, you can use this:

type T int

const (
	TA T = iota
	TB
	TC
	NumT
)

func AllTs() []T {
	ts := make([]T, NumT)
	for i := 0; i &lt; int(NumT); i++ {
		ts[i] = T(i)
	}
	return ts
}

You can also cache the output in e.g. init(). This will only work when all constants are initialised with iota in order. If you need something that works for all cases, use an explicit slice.

答案2

得分: 8

在运行时无法使用reflect包来实现此功能。你可以定义一个列表:

const (
    Created State = iota
    Modified
    Deleted
)
var allStates = []State{Created, Modified, Deleted}

你还可以进一步添加一个字符串表示,或者其他任意数量的内容。

你可以尝试从源代码中生成这样的列表,以便更容易进行维护,但我通常认为这样做并不能节省足够的时间。已经有一些工具(例如stringer)可以完成部分工作。

英文:

There is no way to do this at runtime, as the reflect package cannot be used for it. You could define a list:

const(
    Created State = iota
    Modified
    Deleted
)
var allStates = []State{Created, Modified, Deleted}

You may go further and add in a string representation, or any number of other things.

You may be able to generate such a list from the source to make maintenance easier, but I generally don't think that saves enough time to be worth it. There are tools like stringer that can already do some of that.

答案3

得分: 3

由于你提到了一个测试用例,我假设你已经有了类型和定义常量的文件。我在类似的问题上使用了以下方法(go playground):

package main

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"log"
	"strconv"
	"strings"
)

type InterestingType uint64

const const_go = `
package p

type InterestingType uint64

const (
	A InterestingType = iota << 1
	B
	C
)

type UninterestingType int

const (
    D UninterestingType = iota
    E
)
`

func main() {
	constantValues := []InterestingType{}
	ConstantsOf("InterestingType", const_go, func(v string) {
		value, err := strconv.ParseUint(v, 0, 64)
		if err != nil {
			log.Fatal(err)
		}
		constantValues = append(
            constantValues, InterestingType(value))
	})
	fmt.Printf("%#v\n", constantValues)
}

func ConstantsOf(ctype string, file string, value func(string)) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "const.go", file, 0)
	if err != nil {
		log.Fatal(err)
	}

	// 获取类型信息。
	conf := types.Config{Importer: importer.Default()}
	info := &types.Info{
		Defs: make(map[*ast.Ident]types.Object),
	}
	_, err = conf.Check("p", fset, []*ast.File{f}, info)
	if err != nil {
		log.Fatal(err)
	}

	for _, d := range f.Decls {
		for _, s := range d.(*ast.GenDecl).Specs {
			v, ok := s.(*ast.ValueSpec)
			if !ok {
				continue
			}
			for _, name := range v.Names {
				c := info.ObjectOf(name).(*types.Const)
				if strings.HasSuffix(c.Type().String(), ctype) {
					value(c.Val().ExactString())
				}
			}
		}
	}
}
英文:

Since you talking about a test-case I assume you have the type available as well as the file where the constants are defined in. I used for a similar problem the following approach (go playground):

package main
import (
&quot;fmt&quot;
&quot;go/ast&quot;
&quot;go/importer&quot;
&quot;go/parser&quot;
&quot;go/token&quot;
&quot;go/types&quot;
&quot;log&quot;
&quot;strconv&quot;
&quot;strings&quot;
)
type InterestingType uint64
const const_go = `
package p
type InterestingType uint64
const (
A InterestingType = iota &lt;&lt; 1
B
C
)
type UninterestingType int
const (
D UninterestingType = iota
E
)
`
func main() {
constantValues := []InterestingType{}
ConstantsOf(&quot;InterestingType&quot;, const_go, func(v string) {
value, err := strconv.ParseUint(v, 0, 64)
if err != nil {
log.Fatal(err)
}
constantValues = append(
constantValues, InterestingType(value))
})
fmt.Printf(&quot;%#v\n&quot;, constantValues)
}
func ConstantsOf(ctype string, file string, value func(string)) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, &quot;const.go&quot;, file, 0)
if err != nil {
log.Fatal(err)
}
// Obtain type information.
conf := types.Config{Importer: importer.Default()}
info := &amp;types.Info{
Defs: make(map[*ast.Ident]types.Object),
}
_, err = conf.Check(&quot;p&quot;, fset, []*ast.File{f}, info)
if err != nil {
log.Fatal(err)
}
for _, d := range f.Decls {
for _, s := range d.(*ast.GenDecl).Specs {
v, ok := s.(*ast.ValueSpec)
if !ok {
continue
}
for _, name := range v.Names {
c := info.ObjectOf(name).(*types.Const)
if strings.HasSuffix(c.Type().String(), ctype) {
value(c.Val().ExactString())
}
}
}
}
}

答案4

得分: -1

package main

import (
	"fmt"
)

type State int

const (
	Created State = iota
	Modified
	Deleted
)

func (s State) Name() (name string) {
	switch s {
	case Created:
		name = "创建"
	case Modified:
		name = "修改"
	case Deleted:
		name = "删除"
	}

	return
}

func main() {
	states := States()
	fmt.Println(states)
}

func States() (states []State) {
	state := State(0)

	for {
		name := state.Name()
		if name == "" {
			break
		}

		states = append(states, state)
		state++
	}
	return
}

<details>
<summary>英文:</summary>

package main

import (
"fmt"
)

type State int

const (
Created State = iota
Modified
Deleted
)

func (s State) Name() (name string) {
switch s {
case Created:
name = "created"
case Modified:
name = "modified"
case Deleted:
name = "deleted"
}

return

}

func main() {
states := States()
fmt.Println(states)
}

func States() (states []State) {
state := State(0)

for {
name := state.Name()
if name == &quot;&quot; {
break
}
states = append(states, state)
state++
}
return

}


</details>

huangapple
  • 本文由 发表于 2017年8月26日 04:03:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/45888678.html
匿名

发表评论

匿名网友

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

确定