这段代码:
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(),因此它不是a fmt.Stringer并打印其本机表示形式。但是String()对于我要打印的每个结构都非常繁琐。更糟糕的是,String()如果添加或删除某些字段,则必须更新。有没有更简单的方法来打印带有其字段的结构的结构String()?
A
String()
fmt.Stringer
这就是该fmt包的实现方式,因此您无法更改。
fmt
但是,您可以编写一个使用反射(reflect包)来遍历struct的字段的辅助函数,并可以String()在具有此类方法的字段上调用该方法。
reflect
示例实现:
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,您可以执行以下操作:
struct
fmt.Println(PrintStruct(a, true))
您也可以选择将一个String()方法添加到您的结构中,只需调用我们的PrintStruct()函数即可:
PrintStruct()
func (a A) String() string { return PrintStruct(a, true) }
每当您更改结构时,您都无需对String()方法进行任何操作,因为它使用反射来动态遍历所有字段。
笔记:
由于我们使用反射,因此您必须导出t time.Time字段以使其正常工作(还添加了一些额外的字段用于测试):
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