83

目前我正在使用这个辅助函数来检查 nil 和 nil 接口

func isNil(a interface{}) bool {
  defer func() { recover() }()
  return a == nil || reflect.ValueOf(a).IsNil()
}

由于reflect.ValueOf(a).IsNil()如果值的 Kind 不是Chan, Func, Map, Ptr, Interfaceor时会发生恐慌Slice,因此我投入了 deferredrecover()来捕捉那些。

有没有更好的方法来实现这个检查?它认为应该有更直接的方法来做到这一点。

4

7 回答 7

43

例如,请参阅golang-nuts 邮件列表中此线程中Kyle 的回答。

简而言之:如果您从不存储(*T)(nil)在接口中,那么您可以可靠地使用与 nil 的比较,无需使用反射。另一方面,将无类型 nil 分配给接口总是可以的。

于 2012-11-20T15:43:14.887 回答
26

如果之前的选项都不适合您,那么到目前为止我能想到的最好的方法是:

if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil())

至少它检测到 (*T)(nil) 个案例。

于 2018-05-23T11:21:22.703 回答
9

不使用反射的两种解决方案:

将代码复制并粘贴到编辑器中:https: //play.golang.org/以查看实际情况。

1:在接口中添加“IsInterfaceNil()”函数。

2:使用A“类型开关”

下面的代码:

一一一一一一一一一一一一一一一一一一一一

示例 #1:IsInterfaceNil()

一一一一一一一一一一一一一一一一一一一一

//:Example #1:
//:I prefer this method because the 
//:TakesInterface function does NOT need to know
//:about all the different implementations of
//:the interface.
package main;
import "fmt";

func main()(){

    var OBJ_OK *MyStruct = &( MyStruct{} );
    var NOT_OK *MyStruct = nil;
    
    //:Will succeed:
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    TakesInterface( NOT_OK );

}

func TakesInterface( input_arg MyInterface ){

    if( input_arg.IsInterfaceNil() ){
        panic("[InputtedInterfaceIsNil]");
    }
    
    input_arg.DoThing();
}

type MyInterface interface{
    DoThing()()
    IsInterfaceNil()(bool)
}
type MyStruct struct{}
func(f *MyStruct)DoThing()(){
    fmt.Println("[MyStruct.DoThing]");
}
func(f *MyStruct)IsInterfaceNil()(bool){
    if(nil==f){ return true; }
    return false;
}

一一一一一一一一一一一一一一一一一一一一

示例 #2:类型切换

一一一一一一一一一一一一一一一一一一一一

//:Example #2:
//:This will also work, but the function taking
//:the interface needs to know about all 
//:implementations. This defeats a bit of the
//:decoupling from implementation that an
//:interface offers, but if you are just using
//:interfaces for polymorphism, it's probably
//:an okay way to go. (opinion)
package main;
import "fmt";

func main()(){

    //:Will succeed:
    var OBJ_OK *IMPLMENTS_INTERFACE_01 = 
             &( IMPLMENTS_INTERFACE_01{} );
    TakesInterface( OBJ_OK );
    
    //:Will fail:
    var NOT_OK *IMPLMENTS_INTERFACE_01 = nil;
    TakesInterface( NOT_OK );
}

func TakesInterface( hasDoThing MyInterface ){

    //:THIS WILL NOT WORK:
    if(nil==hasDoThing){
        panic("[This_Error_Message_Will_Never_Happen]");
    }
    
    //:TYPE SWITCH TO THE RESCUE:
    switch v := hasDoThing.(type){
    
        case (*IMPLMENTS_INTERFACE_01): 
        if(nil==v){ panic("[Nil_PTR_01]"); }
        
        case (*IMPLMENTS_INTERFACE_02): 
        if(nil==v){ panic("[Nil_PTR_02]"); }
        
        case (*IMPLMENTS_INTERFACE_03): 
        if(nil==v){ panic("[Nil_PTR_03]"); }
        
        default: 
            panic("[UnsupportedInterface]");
    }
    
    hasDoThing.DoThing();
    
}

type IMPLMENTS_INTERFACE_01 struct{};
type IMPLMENTS_INTERFACE_02 struct{};
type IMPLMENTS_INTERFACE_03 struct{};
func (f *IMPLMENTS_INTERFACE_01)DoThing()(){
    fmt.Println( "DoingTheThing_01" );
}
func (f *IMPLMENTS_INTERFACE_02)DoThing()(){
    fmt.Println( "DoingTheThing_02" );
}
func (f *IMPLMENTS_INTERFACE_03)DoThing()(){
    fmt.Println( "DoingTheThing_03" );
}

type MyInterface interface{
    DoThing()()
}

更新: 在我的代码库中实施后,我发现#2(类型切换)是最好的解决方案。特别是因为我不想在我正在使用的绑定库中编辑 glfw.Window 结构。这是我的用例的粘贴箱。为我的非标准编码风格道歉。https://pastebin.com/22SUDeGG

于 2018-02-28T00:45:48.410 回答
0

这是此示例解决方案的接口定义:

package checker

import (
    "errors"

    "github.com/rs/zerolog"
)

var (
    // ErrNilChecker returned if Check invoked on a nil checker
    ErrNilChecker = errors.New("attempted Check with nil Checker")

    // ErrNilLogger returned if the Check function is provide a nil logger
    ErrNilLogger = errors.New("nil logger provided for Check")
)

// Checker defines the interface
type Checker interface {
    Check(logger *zerolog.Logger) error
}

我们的检查器实现之一支持检查器的聚合。但是测试发现了与此线程相同的问题。如果简单的 nil 检查失败,此解决方案将使用reflect包,利用reflect.Value类型来解决问题。

// AggregateChecker implements the Checker interface, and
//  supports reporting the results of applying each checker
type AggregateChecker struct {
    checkers []Checker
}

func (ac *AggregateChecker) Add(aChecker Checker) error {
    if aChecker == nil {
        return ErrNilChecker
    }

    // It is possible the interface is a typed nil value
    // E.g. checker := (&MyChecker)(nil)
    t := reflect.TypeOf(aChecker)
    if reflect.ValueOf(aChecker) == reflect.Zero(t) {
        return ErrNilChecker
    }

    ac.checkers = append(ac.checkers, aChecker)
    return nil
}
于 2019-02-14T14:39:47.010 回答
0

可能是您尝试在填充界面的函数中使用错误:

包装一个

func getObj() (obj *someObject, err error) {
   obj = db.getA()
   if obj == nil {
      err = fmt.Errorf("Some error")
   }
   return
}

包 b

import a

var i interface{}

i, err = a.getObj()
if err != nil {
   // catch err
} else {
   // do next step
}
于 2021-08-06T10:47:51.423 回答
-1

认为inboundData是你的界面

使用len()函数检查界面是否有数据

if inboundData != nil && len(inboundData) > 0 {
    // ... do stuff | data is present
} else {
    // ... data is not present
}
于 2020-12-14T13:35:09.490 回答
-1
func main() {
    var foo interface{}
    fmt.Println(reflect.TypeOf(foo) == nil) // true

    type Bar struct{}
    var bar *Bar
    fmt.Println(reflect.TypeOf(bar) != nil) // true
}
于 2021-07-20T13:29:49.630 回答