0x0

反射:

golang的reflect包实现了运行时的反射能力。reflect包中有两个重要函数:

  • reflect.TypeOf 获取数据类型信息,其类型为reflect.Type
  • reflect.ValueOf 获取数据运行时的信息,其类型为reflect.Value

Tag

golang的struct中的每一个字段(field)后添加一段注释,称为 field tag,最经典就是构建json的struct,例如:

type Login struct{
    UserName string `json:"user_name"`
    Password string `json:"password"`
}

其中 json:"user_name" json:"password"就是 field tag

0x1

假设现有一 struct

type Note struct {
    Time    time.Time
    Content string
    Remarks string
}

我们想要指定一些字段非空,例如:Content != ""

按照一般通用的方法就是一个一个用if判断:

if content != "" { ... } else { ... }

如果只有几个字段需要这样判断还可以这样写。但如果有许多个字段或不同字段有不同类型,这样写的话就是一堆 if else,就不是很优雅。

0x2

现在可以使用反射来更为优雅实现这一功能。只需要在struct需要非空的字段后添加一个 tag来标记,这个tag可以自定义,如:

type Note struct {
    Time    time.Time `must:"true"`
    Content string    `must:"true"`
    Remarks string
}

其中 must:"true"为我们自定义的tag,表示该字段非空。

0x3

我们可以通过反射来获取struct字段的tag

fmt.Println(reflect.TypeOf(&note).Elem().Field(0).Tag.Get("must"))    //获取第一个字段tag中"must"的值

0x4

完整实现代码:


type Note struct {
    Time    time.Time `must:"true"`
    Content string    `must:"true"`
    Remarks string
}

func validator(value interface{}) {
    val := reflect.ValueOf(value).Elem() //获取字段值
    typ := reflect.TypeOf(value).Elem()  //获取字段类型
    // 遍历struct中的字段
    for i := 0; i < typ.NumField(); i++ {
        // 当struct中的tag为 must:"true" 且当前字段值为空值时,输出
        if typ.Field(i).Tag.Get("must") == "true" && val.Field(i).IsZero() {
            fmt.Println("Fail:", typ.Field(i).Name, "is null")
            return
        }
    }
    fmt.Println("Pass")
}

func main() {
    note1 := Note{
        Time:    time.Now(),
        Content: "content",
        Remarks: "",
    }
    validator(&note1)
    // Pass

    note2 := Note{
        Time:    time.Now(),
        Content: "",
        Remarks: "",
    }
    validator(&note2)
    // Fail: Content is null
}
Last modification:August 20, 2021
如果觉得我的文章对你有用,请随意赞赏