【问题标题】:How to extract the interface type name and package using reflection?如何使用反射提取接口类型名称和包?
【发布时间】:2019-05-07 10:58:30
【问题描述】:

我需要知道类型名称及其使用反射的路径。 Type 类型有一个 Name() 和 PkgPath() 方法,但如果类型是接口,它们都返回空。

但是,如果我反映一个函数并提取其参数的类型信息,我会得到正确的类型信息。我应该假设这是前一种情况的错误吗?无论上下文(例如类型函数参数或值的类型),TypeOf 不应该返回相同的类型信息吗?

我知道类型断言,但我并不总是有一个值来做断言,所以我需要使用 reflect.Type 信息。

package main

import (
    "fmt"
    "reflect"
    "golang.org/x/net/context"
)

func main() {
    c := reflect.TypeOf(withValue(""))
    fn := func(context.Context){}
    fc := reflect.TypeOf(fn).In(0)
    fmt.Println(isContext(c),  isContext(fc), c, fc)
}

func isContext(r reflect.Type) bool {
    return r.PkgPath() == "golang.org/x/net/context" && r.Name() == "Context"
}


func withValue(v interface{}) context.Context {
    return context.WithValue(context.TODO(), "mykey", v)
}

打印

false true *context.valueCtx context.Context

【问题讨论】:

标签: reflection go


【解决方案1】:

这是一些工作代码:https://play.golang.org/p/ET8FlguA_C

package main

import (
    "fmt"
    "reflect"
)

type MyInterface interface {
    MyMethod()
}

type MyStruct struct{}

func (ms *MyStruct) MyMethod() {}

func main() {
    var structVar MyInterface = &MyStruct{}
    c := reflect.TypeOf(structVar)

    fn := func(MyInterface) {}
    fc := reflect.TypeOf(fn).In(0)

    fmt.Println(isMyInterface(c), isMyInterface(fc), c, fc)
    // OP expects : "true true main.MyInterface main.MyInterface"
}

func isMyInterface(r reflect.Type) bool {
    // TypeOf trick found at https://groups.google.com/forum/#!topic/golang-nuts/qgJy_H2GysY
    return r.Implements(reflect.TypeOf((*MyInterface)(nil)).Elem())
}

在我找到reflect 的实际解决方案之前,这是我的答案。 我会把它放在这里,因为我认为它仍然有一些有趣的部分。


第一件事:对于c,r.PkgPath() 和 r.Name() 为空,因为基础类型是指针 (*context.valueCtx)。

要解决这个问题,您可以使用c := reflect.Indirect(reflect.ValueOf(withValue(""))).Type()

但这并不能使isContext(c) 为真,因为你有r.PkgPath() == "golang.org/x/net/context" && r.Name() == "valueCtx"

检查 var 是否实现接口的最佳方法是删除反射并使用如下类型断言:

https://play.golang.org/p/td1YaHHej9

package main

import "fmt"

type MyInterface interface {
    MyMethod()
}

type MyStruct struct{}

func (ms *MyStruct) MyMethod() {}

func main() {
    var structVar MyInterface = &MyStruct{}

    fmt.Println(isMyInterface(structVar))
}

func isMyInterface(object interface{}) bool {
    _, ok := object.(MyInterface)
    return ok
}

您的代码使用函数参数按预期工作,因为没有基础值,因此reflect 使用接口类型。但对于任何具体的 var,它都会使用值的实际类型。

【讨论】:

    【解决方案2】:

    golang中有两种接口,又名efaceiface。而eface是一个空接口,可以简单地表示为interface {}。 iface 是一种至少有一个方法的接口,例如:

    type MyInterface interface {
        Greeting() string
    }
    

    在 golang 实现中,eface 和 iface 都是双字长结构体。 eface 保存数据和数据类型,iface 保存数据、interfacetype 和数据类型。当一个iface 分配给一个eface 时,interfacetype 信息被忽略。仅传递给 eface 的数据和数据类型。

    所以,reflect.TypeOf(i interface{}) 的参数是和 eface,没有接口类型信息(在您的情况下也称为 context.Context)。所以你不能得到原来的interfacetype。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 2020-05-31
      • 2015-02-22
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      • 2013-04-08
      相关资源
      最近更新 更多