2015-10-15 13 views
8

ten kod:Jak wydrukować struct za pomocą String() pól?

type A struct { 
    t time.Time 
} 

func main() { 
    a := A{time.Now()} 
    fmt.Println(a) 
    fmt.Println(a.t) 
} 

drukuje:

{{63393490800 0 0x206da0}} 
2009-11-10 23:00:00 +0000 UTC 

A nie implementuje String(), więc to nie jest fmt.Stringer i drukuje swoją rodzimą reprezentację. Ale jest bardzo żmudne wdrożenie String() dla każdej struktury, którą chcę wydrukować. Co gorsza, muszę zaktualizować String() s, jeśli dodaję lub usuwam niektóre pola. Czy jest łatwiejszy sposób drukowania struktury z polami: "String() s?

Odpowiedz

4

W ten sposób zaimplementowano pakiet fmt, więc nie można tego zmienić.

Ale można napisać funkcję pomocniczą, która używa pakietu refleksji (reflect) do iteracji nad polami struktury i może wywoływać metodę String() na polach, jeśli mają taką metodę.

Przykład wdrożenia:

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() 
} 

Teraz, gdy chcemy wydrukować struct, można zrobić:

fmt.Println(PrintStruct(a, true)) 

Można również wybrać, aby dodać metodę String() do swojej struktury, która po prostu musi zadzwoń pod naszą PrintStruct() funkcję:

func (a A) String() string { 
    return PrintStruct(a, true) 
} 

Kiedy zmieniasz strukturę, nie musisz nic robić z metodą String(), ponieważ używa ona refleksji, aby dynamicznie przechodzić przez wszystkie pola.

Uwagi:

Skoro jesteśmy przy użyciu odbicia, trzeba wyeksportować pole t time.Time dla tej pracy (również dodać kilka dodatkowych pól dla celów testowych):

type A struct { 
    T   time.Time 
    I   int 
    unexported string 
} 

Testing to:

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)) 

Output (spróbuj go na 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

Dokładnie tego staram się unikać. Chcę użyć 'time.Time'' 'String()', zamiast ujawniać jego wewnętrznej implementacji. –

+0

@ LaiYu-Hsuan Masz rację. Zobacz edytowaną odpowiedź. – icza

Powiązane problemy