如何优雅的判断变量是否为空呢?如下两个方法即可(先给着急用答案)

func IsNil(val interface{}) bool {
    if nil == val {
        return true
    }
    return reflect.ValueOf(val).IsNil()
}

func IsNotNil(val interface{}) bool {
    return !IsNil(val)
}

nil 判断在 go 的日常开发编码中是比较常见的,不管是错误返回,亦是未初始化的 SliceMapChanPointerFuncinterface{} 都会涉及到判空的场景。

func main() {
    var sli []int
    fmt.Println("slice:", sli == nil, reflect.TypeOf(sli).Kind(), reflect.ValueOf(sli).IsValid())

    var ma map[string]int
    fmt.Println("map:", ma == nil, reflect.TypeOf(ma).Kind(), reflect.ValueOf(ma).IsValid())

    var ch chan int
    fmt.Println("chan:", ch == nil, reflect.TypeOf(ch).Kind(), reflect.ValueOf(ch).IsValid())

    var user *struct {
        Name string
        Age  uint8
    }
    fmt.Println("pointer:", user == nil, reflect.TypeOf(user).Kind(), reflect.ValueOf(user).IsValid())

    var fun func() string
    fmt.Println("func:", fun == nil, reflect.TypeOf(fun).Kind(), reflect.ValueOf(fun).IsValid())

    var err error
    //fmt.Println(err == nil, errors.Is(err, nil))
    fmt.Println("error:", err == nil, reflect.TypeOf(err), reflect.ValueOf(err).IsValid())

    var itf interface{}
    fmt.Println("interface{}:", itf == nil, reflect.TypeOf(itf), reflect.ValueOf(itf))
}
slice: true slice true
map: true map true
chan: true chan true
pointer: true ptr true
func: true func true
error: true <nil> false
interface{}: true <nil> <invalid reflect.Value>

注解说明一下

func IsNil(val interface{}) bool {
    // type && value 都为 nil 的场景 nil error / interface{}
    // type nil 则 value 必 nil, 此时可直接与 nil 比较
    // 等价于 nil == reflect.TypeOf(val)
    if nil == val {
        return true
    }
    // 其它类型的变量 需要看 value
    return reflect.ValueOf(val).IsNil()
}

func IsNotNil(val interface{}) bool {
    return !IsNil(val)
}

需要注意的是:为了兼容所有类型的变量的判空(我们的目的就是判是否为空),所以参数类型为 interface{},但 ”空接口“变量 并不能简单的使用 nil == val 来判断是否为空,因为 interface{} 有两个属性 typevalue。只有当 typenil && valuenil 时,才可以直接使用 nil == val 来做判空,而非 interface{} 类型的变量转为此类型时,type 会被设为对应的数据原型,故此时我们需要进一步判断 value 是否为空。

例如:

  1. 当我们将一个 空指针 var p *int 传值给 interface{} 时会得被转为如下状态:{type: pointer, value: nil},此时因为 typepointer,所以直接与 nil 等值比较会得 false,应该进一步比较 value 是否为 nil 来得到期望结果,故需要通过 reflect 获取 value 进行值的判空。
  2. error 类型实为 interface { Error() string },当声明了一个变量 var err error 时,其实值还是一个 {type: nil,value: nil}interface{},可以使用 reflect.Typeof(err), reflect.Valueof(err) 来具体查看。

引申:
当我们使用 reflect 来反射解析变量获取 TypeValue 时,一定要先判断 reflect.Typeof(val)reflect.Valueof(val).IsValid()== nil 来判断是否为 nil 值的 interface{},如果是则没有反射的必要了。

func main() {
    var val error
    if nil == val {
        fmt.Println("val is nil:", true)
    }
    if nil == reflect.Typeof(val) {
        fmt.Println("val is nil:", true)
    }
    if !reflect.Valueof(val).IsValid() {
        fmt.Println("val is nil:", true)
    }
    if reflect.Valueof(val).IsValid() {
        fmt.Println("val is nil:", reflect.Valueof(val).IsNil())
        fmt.Println("val is zero:", reflect.Valueof(val).IsZero())
    }
}

big_cat
1.7k 声望130 粉丝

规范至上