如何在Golang中使用字段的String()方法打印结构体

如何在Golang中使用字段的String()方法打印结构体

package main

import (
	"bytes"
	"fmt"
	"reflect"
)
type Email string

type Person struct {
	email Email
	name  string
}

func (e Email) String() string {
	return "super secret!"
}

func main() {
	foo := Person{
		email: "aaa@bbb.com",
		name:  "Bar",
	}
	fmt.Println(foo)
}

期望输出:{super secret! Bar},但实际得到:{aaa@bbb.com Bar}

我也尝试过使用反射,但似乎在使用 Field(i) 时丢失了 Email 的类型信息,它只给出了 Email 的基础类型,即 stringreflect.String,因此调用的 String() 方法并不是 func (e Email) String() string

func StructString(s any) string {
	v := reflect.ValueOf(s)
	t := v.Type()

	if t.Kind() != reflect.Struct {
		panic("todo")
	}

	b := &bytes.Buffer{}
	b.WriteString("{")
	for i := 0; i < v.NumField(); i++ {
		if i > 0 {
			b.WriteString(" ")
		}
		// 这里的字段
		valField := v.Field(i)
		switch valField.Kind() {
		case reflect.String:
			b.WriteString(valField.String())
		default:
      // 此处无关...
		}
	}
	b.WriteString("}")
	return b.String()
}

更多关于如何在Golang中使用字段的String()方法打印结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你需要为你的结构体 Person 实现 Stringer 接口。

更多关于如何在Golang中使用字段的String()方法打印结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


要实现通过反射调用自定义的 String() 方法,需要检查字段是否实现了 fmt.Stringer 接口,而不是直接判断其类型。以下是修改后的 StructString 函数:

func StructString(s any) string {
    v := reflect.ValueOf(s)
    t := v.Type()

    if t.Kind() != reflect.Struct {
        panic("todo")
    }

    b := &bytes.Buffer{}
    b.WriteString("{")
    for i := 0; i < v.NumField(); i++ {
        if i > 0 {
            b.WriteString(" ")
        }
        
        field := v.Field(i)
        // 检查字段是否实现了fmt.Stringer接口
        if stringer, ok := field.Interface().(fmt.Stringer); ok {
            b.WriteString(stringer.String())
        } else {
            // 对于未实现Stringer的字段,使用默认格式化
            fmt.Fprintf(b, "%v", field.Interface())
        }
    }
    b.WriteString("}")
    return b.String()
}

使用示例:

func main() {
    foo := Person{
        email: "aaa@bbb.com",
        name:  "Bar",
    }
    fmt.Println(StructString(foo)) // 输出: {super secret! Bar}
}

对于更通用的解决方案,可以直接使用 fmt.Fprintf%v 动词,它会自动调用值的 String() 方法:

func StructString(s any) string {
    v := reflect.ValueOf(s)
    t := v.Type()

    if t.Kind() != reflect.Struct {
        panic("todo")
    }

    b := &bytes.Buffer{}
    b.WriteString("{")
    for i := 0; i < v.NumField(); i++ {
        if i > 0 {
            b.WriteString(" ")
        }
        fmt.Fprintf(b, "%v", v.Field(i).Interface())
    }
    b.WriteString("}")
    return b.String()
}

如果需要在 fmt.Println(foo) 中直接输出期望的结果,可以为 Person 结构体实现 String() 方法:

func (p Person) String() string {
    return fmt.Sprintf("{%v %s}", p.email, p.name)
}

func main() {
    foo := Person{
        email: "aaa@bbb.com",
        name:  "Bar",
    }
    fmt.Println(foo) // 输出: {super secret! Bar}
}
回到顶部