指向 Go 中接口的指针

yag*_*eek 3 go

我目前正在阅读https://github.com/codegangsta/inject go 包的源代码,以了解该包的工作原理。

我对文件https://github.com/codegangsta/inject/blob/master/inject.go文件有一些疑问,该文件使用 Go 语言的某些元素,我不理解,并且在文档中找不到准确的解释。

// InterfaceOf dereferences a pointer to an Interface type.
// It panics if value is not an pointer to an interface.

func InterfaceOf(value interface{}) reflect.Type {
        t := reflect.TypeOf(value)

        for t.Kind() == reflect.Ptr {
                t = t.Elem()
        }

        if t.Kind() != reflect.Interface {
                panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
        }

        return t
}
Run Code Online (Sandbox Code Playgroud)

我的第一个问题是关于for循环的。为什么它使用带有测试表达式的 for 循环?

第二个与恐慌函数中的消息有关。中提到了“指向接口的指针” (*MyInterface)(nil)。当您检查类型是否实现结构时,我只会在有关“编译时检查结构”的 go 文档中遇到类似的构造:

var _ SomeType = (*SomeInterface)(nil)
Run Code Online (Sandbox Code Playgroud)

我没有找到任何有关带有(*Interface)(nil)接口的声明和指针的信息。

我们该如何解读这个说法呢?与接口指针有什么关系?在哪里可以找到有关接口指针的信息?

nem*_*emo 5

总结两个 答案

循环for

for t.Kind() == reflect.Ptr {
    t = t.Elem()
}
Run Code Online (Sandbox Code Playgroud)

t.Elem()是相当于 的反射*t,那么t只要它保存另一个指针值,这个循环就会取消引用。在循环结束时,t将保存最后一个指针指向的值,不再是指针。

消息

使用不是指向接口的指针的值调用 [...]。(*MyInterface)(nil)

该表达式(*MyInterface)(nil)只是预期参数的一个(措辞不佳的)示例。

语法是转换的语法。在本例中,转换将尝试将值(在本例中nil为 )转换为给定类型 ( *MyInterface)。所以,

(*MyInterface)(nil) 
Run Code Online (Sandbox Code Playgroud)

将为您提供一个零值,*MyInterface其接口类型为MyInterface( play ):

x := (*MyInterface)(nil)
InterfaceOf(x) // MyInterface
Run Code Online (Sandbox Code Playgroud)

当然,这个值并没有指向有意义的地方。

接口实现的编译时检查

为了避免混淆,您展示的结构

var _ SomeType = (*SomeInterface)(nil)

可能不是你想要的。我猜你想要这个:

var _ SomeInterface = (*SomeType)(nil)
Run Code Online (Sandbox Code Playgroud)

此构造允许对某些类型的接口实现进行编译时检查。因此,如果您正在编写某种类型的库,并且想要满足接口而不使用它,则可以使用它来确保您的结构实现该接口。

为什么这有效

首先,var _ someType是一个将由编译器检查的变量,但不会出现在已编译的程序中,并且由于空白标识符_而无法访问:

空白标识符可以像声明中的任何其他标识符一样使用,但它不会引入绑定,因此不会被声明。

这使您能够声明任意数量的这些构造,而不会干扰程序的其余部分。

您可以通过编写以下内容来声明任何类型的指针的零值:

(*T)(nil)
Run Code Online (Sandbox Code Playgroud)

请在播放时查看此示例

接下来,可分配性表示如果是一个接口并且实现 则可x分配给。TTxT

总结一下:

T _ = (*x)(nil)
Run Code Online (Sandbox Code Playgroud)

强制x执行T,因为其他一切都会出错。