检查Go中的nil和nil接口
目前我正在使用这个helper函数来检查nil和nil接口检查Go中的nil和nil接口,go,Go,目前我正在使用这个helper函数来检查nil和nil接口 func isNil(a interface{}) bool { defer func() { recover() }() return a == nil || reflect.ValueOf(a).IsNil() } 由于reflect.ValueOf(a).IsNil()如果值的种类不是Chan,Func,Map,Ptr,接口或切片,我会惊慌失措,因此我加入了延迟recover(),以捕捉这些 是否有更好的方法实现此检查?
func isNil(a interface{}) bool {
defer func() { recover() }()
return a == nil || reflect.ValueOf(a).IsNil()
}
由于reflect.ValueOf(a).IsNil()
如果值的种类不是Chan
,Func
,Map
,Ptr
,接口
或切片,我会惊慌失措,因此我加入了延迟recover()
,以捕捉这些
是否有更好的方法实现此检查?它认为应该有一种更直接的方法来做到这一点。例如,请参见golang nuts邮件列表中Kyle的回答
简而言之:如果您从未在接口中存储(*T)(nil)
,那么您可以可靠地使用与nil的比较,而无需使用反射。另一方面,将非类型的nil分配给接口总是可以的。两种解决方案不使用反射:
将代码复制并粘贴到编辑器中:以查看操作
1:向接口添加“IsInterfaceNil()”函数。
2:使用“类型开关”
下面的代码:
一一一一一一一一一一一一一一一一一一一一强>
//: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;
}
//: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()()
}
示例#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;
}
//: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()()
}
一一一一一一一一一一一一一一一一一一一一强>
//: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;
}
//: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:类型开关
一一一一一一一一一一一一一一一一一一一一强>
//: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;
}
//: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结构。这是我的用例的粘贴箱。
为我的非标准编码风格道歉 如果前面的两个选项都不适用于您,那么到目前为止,我能想到的最好方法是:
if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil())
至少它检测(*T)(无)情况。这是此exmaple解决方案的接口定义:
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实现支持Checker的聚合。但是测试发现了与此线程相同的问题。
如果简单的零检查失败,此解决方案将使用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
}
将inboundData
视为您的界面
if inboundData != nil && len(inboundData) > 0 {
// ... do stuff | data is present
} else {
// ... data is not present
}
使用len()
功能检查界面中是否有数据
if inboundData != nil && len(inboundData) > 0 {
// ... do stuff | data is present
} else {
// ... data is not present
}
我不明白。。。为什么一个简单的a==nil
不起作用?@SongGao:OP正在检查两件不同的事情:1)如果a
是nil接口本身(在这种情况下a==nil
将为真),或者如果a
是一个非nil接口,其底层值是通道、函数、指针或片类型的nil
值(在这种情况下,a==nil
将为假)被接受的答案是不正确的,我遇到了同样的问题。不幸的是,正确的答案是检查每个案例。那么[]T(nil)
,map[T]T(nil)
,func()(nil)
和chant(nil)呢
?这看起来像是一个等待发生的恶意bug。很抱歉我的无知,你能进一步解释一下“另一方面,给接口分配非类型的nil总是可以的”的含义吗?我想知道这意味着什么……首先,我不知道nil可以被键入或非键入……我相信它是(nil)任何指针的特殊值types@Victor它们只是表示可以将nil存储在接口变量中。例如:var thing interface{};thing=nil
。这是完全合法的。thing
的“类型”为interface{}
,但没有基础类型。但是,不能将nil存储在非类型化变量中,如thing:=nil
。nil是“类型化的”因为它需要某种类型的指针。比如nil int指针或nil string指针。如果没有接口,你就不能有一个指向未知类型的nil指针。但是,它是一种类型化的……这确实令人困惑。@Nathalutterman谢谢你的解释,我明白了这个概念。仍然令人困惑,因为nil是一个不是类型的值(至少在golang世界之外…)“untyped nil”的安装我建议使用“literal nil”这个词,因为它是放在表达式中的值本身,而不是放在一个变量、constanst或函数调用返回值中的值。这并不能解决普通接口{}的问题,这就是问题所在。我同意。这是一个解决办法。我对“简言之,[…]从不在接口中存储(*t)(nil)”的公认答案不满意,只要c
不是指向片的类型指针等。在这种情况下,类似reflect.Ptr(reflect.ValueOf(c))和&reflect.ValueOf(c.Elem().IsNil()的逻辑
是必需的。我怀疑是否涵盖了所有其他情况……1.)这不适用于接口{}
。我假设inboundData
在您的例子中是一个切片或一个映射。2.)它不知道什么时候某个东西是nil
,什么时候某个贴图或切片是空的。3.)对于地图和切片,如果您只想知道是否有数据,根本不需要执行nil
-在len(…)
之前检查。在这种情况下,只需执行len(…)
。