检查Go中的nil和nil接口

时间:2012-11-20 15:21:55

标签: go

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

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

如果reflect.ValueOf(a).IsNil()值不是ChanFuncMapPtrInterface或{{},那么Slice会发生恐慌1}},我把延迟的recover()扔进去抓住那些。

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

7 个答案:

答案 0 :(得分:28)

在golang-nuts邮件列表中查看Kyle在此thread中的答案。

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

答案 1 :(得分:9)

如果以前的选项都不适合你,那么到目前为止我能做到的最好的选择是:

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

至少检测到(* T)(无)个案。

答案 2 :(得分:3)

两种解决方案不使用反射

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

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

2:使用“类型开关”

以下代码:

<强>一一一一一一一一一一一一一一一一一一一一

示例#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:键入Switch

<强>一一一一一一一一一一一一一一一一一一一一

//: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

答案 3 :(得分: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
}

我们的Checker实现之一支持Checkers的聚合。但是测试发现了与此线程相同的问题。 如果简单的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
}

答案 4 :(得分:0)

考虑inboundData作为您的界面

使用len()函数检查接口中是否有数据

if inboundData != nil && len(inboundData) > 0 {
    // ... do stuff | data is present
} else {
    // ... data is not present
}

答案 5 :(得分:0)

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
}

答案 6 :(得分: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
}