2015-10-15 38 views
8

此代码:如何使用字段的String()打印结构?

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并打印其本机表示。但是对于我想要打印的每个结构实现String()都非常繁琐。更糟的是,如果我添加或删除一些字段,我必须更新String()。有没有更简单的方法来打印一个结构,其字段'String() s?

回答

4

这是如何实现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 
+0

这正是我试图避免的。我想使用'time.Time'的'String()',而不是公开它的内部实现。 –

+0

@赖玉轩你说得对。见编辑的答案。 – icza