检查Go中的nil和nil接口

Eri*_*ner 48 go

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

func isNil(a interface{}) bool {
  defer func() { recover() }()
  return a == nil || reflect.ValueOf(a).IsNil()
}
Run Code Online (Sandbox Code Playgroud)

由于reflect.ValueOf(a).IsNil()恐慌,如果该值的种类比其他任何东西Chan,Func,Map,Ptr,Interface或者Slice,我在推迟扔recover()赶上那些.

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

zzz*_*zzz 32

在golang-nuts邮件列表中查看Kyle在此主题中的答案.

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

  • 怎么样`[] T(nil)`,`map [T] T(nil)`,`func()(nil)`和`chan T(nil)`? (5认同)
  • 这看起来像一个等待发生的邪恶的错误. (5认同)
  • @Victor他们只是意味着你可以将nil存储在接口变量中。如:`var thing interface{}; 东西=零`。这是完全合法的。`thing` 具有 `interface{}` 的“类型”,但没有_底层_类型。但是,您不能将 nil 存储在无类型变量中,例如 `thing := nil`。Nil 是“类型化”的,因为它需要某种类型来配合它。就像 nil int 指针或 nil string 指针一样。你不能只拥有一个指向某种未知类型的 nil 指针,而它不是一个接口。但是,它是打字的……这绝对令人困惑。 (5认同)
  • @NathanLutterman 谢谢你的解释,我明白了。仍然令人困惑,因为 nil 是一个值而不是类型(至少在 golang 世界之外......)。代替“无类型 nil”,我建议使用“字面 nil”一词,因为它本身就是放置在表达式中的值,而不是放置在一个变量、常量或函数调用返回值中的值。 (3认同)

小智 14

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

if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil())
Run Code Online (Sandbox Code Playgroud)

至少它检测到(*T)(无)的情况.

  • 只要 `c` 不是指向切片等的类型化指针,就可以工作。在这种情况下,像 `reflect.Ptr(reflect.ValueOf(c)) && Reflect.ValueOf(c).Elem().IsNil( )` 是必需的。我怀疑所有其他案件是否都涵盖在内...... (2认同)

J.M*_*SON 5

Two solutions NOT using reflection:

Copy and paste code into editor at: https://play.golang.org/ to see in action.

1: Add an "IsInterfaceNil()" function to interface.

2: Use A "type switch"

CODE BELOW:

????????????????????

EXAMPLE #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;
}
Run Code Online (Sandbox Code Playgroud)

????????????????????

EXAMPLE #2: Type 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()()
}
Run Code Online (Sandbox Code Playgroud)

UPDATE: After implementing in my code base, I found #2 (type switch) to be best solution. Specifically because I DON'T want to EDIT the glfw.Window struct in the bindings library I am using. Here is a paste-bin of my use-case. Apologies for my non-standard coding style. https://pastebin.com/22SUDeGG

  • 这并不能解决普通接口{}的问题,这就是问题所在 (2认同)