这段代码:
type A struct {
t time.Time
}
func main() {
a := A{time.Now()}
fmt.Println(a)
fmt.Println(a.t)
}
打印:
{{63393490800 0 0x206da0}}
2009-11-10 23:00:00 +0000 UTC
A
未实现 String()
,因此它不是 fmt.Stringer
并打印其 native 表示。但是为我想要打印的每个结构体实现 String()
是非常乏味的。更糟糕的是,如果我添加或删除某些字段,我必须更新 String()
。有没有更简单的方法来打印结构及其字段的 String()
?
最佳答案
这就是 fmt
包的实现方式,因此您无法更改它。
但是你可以编写一个辅助函数,它使用反射(reflect
包)来遍历结构的字段,并且可以在字段上调用 String()
方法(如果它们有这样的方法)一种方法。
示例实现:
func PrintStruct(s interface{}, names bool) string {
v := reflect.ValueOf(s)
t := v.Type()
// To avoid panic if s is not a struct:
if t.Kind() != reflect.Struct {
return fmt.Sprint(s)
}
b := &bytes.Buffer{}
b.WriteString("{")
for i := 0; i < v.NumField(); i++ {
if i > 0 {
b.WriteString(" ")
}
v2 := v.Field(i)
if names {
b.WriteString(t.Field(i).Name)
b.WriteString(":")
}
if v2.CanInterface() {
if st, ok := v2.Interface().(fmt.Stringer); ok {
b.WriteString(st.String())
continue
}
}
fmt.Fprint(b, v2)
}
b.WriteString("}")
return b.String()
}
现在当你想打印一个struct
时,你可以这样做:
fmt.Println(PrintStruct(a, true))
你也可以选择添加一个 String()
方法到你的结构中,它只需要调用我们的 PrintStruct()
函数:
func (a A) String() string {
return PrintStruct(a, true)
}
每当您更改结构时,您不必对 String()
方法执行任何操作,因为它使用反射动态遍历所有字段。
注意事项:
由于我们使用的是反射,因此您必须导出 t time.Time
字段才能正常工作(还添加了一些额外的字段用于测试目的):
type A struct {
T time.Time
I int
unexported string
}
测试它:
a := A{time.Now(), 2, "hi!"}
fmt.Println(a)
fmt.Println(PrintStruct(a, true))
fmt.Println(PrintStruct(a, false))
fmt.Println(PrintStruct("I'm not a struct", true))
输出(在 Go Playground 上尝试):
{T:2009-11-10 23:00:00 +0000 UTC I:2 unexported:hi!}
{T:2009-11-10 23:00:00 +0000 UTC I:2 unexported:hi!}
{2009-11-10 23:00:00 +0000 UTC 2 hi!}
I'm not a struct
关于string - 如何使用字段的 String() 打印结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33142594/