How to extract the interface type name and package using reflection?

huangapple go评论157阅读模式
英文:

How to extract the interface type name and package using reflection?

问题

我需要使用反射来获取类型名称和路径。类型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
英文:

I need to know the type name and its path using reflection. type Type has a Name() and PkgPath() method but both of them return empty if the type is an interface.

However if I reflect a function and extract the type information of its arguments I get the correct type information. Should I assume it's a bug in the former case? Shouldn't TypeOf return the same type information regardless the context(e.g. type function parameter or type of a value) ?

I'm aware of type assertion but I don't always have a value to do the assertion so I need to work with reflect.Type information.

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)
}

Prints

false true *context.valueCtx context.Context

答案1

得分: 5

这是一段工作代码: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的实际解决方案之前,这是我的答案。我将它保留在这里,因为我认为它仍然有一些有趣的部分。

首先要说明的是,对于cr.PkgPath()r.Name()是空的,因为底层类型是指针(*context.valueCtx)。

要修复这个问题,你可以使用c := reflect.Indirect(reflect.ValueOf(withValue(""))).Type()

但是,这并不会使isContext(c)true,因为你随后会得到r.PkgPath() == "golang.org/x/net/context" && r.Name() == "valueCtx"

检查变量是否实现了接口的最佳方法是放弃使用反射,而是使用类型断言,像这样:

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使用接口类型。但对于任何具体的变量,它将使用值的实际类型。

英文:

Here is some working code: 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())
}

Here is my answer before I found an actual solution with reflect.
I'm gonna let it here because I think it still has some interesting parts.


First things first: for c, r.PkgPath() and r.Name() are empty because the underlying type is a pointer (*context.valueCtx).

To fix that, you can use c := reflect.Indirect(reflect.ValueOf(withValue(""))).Type()

But that does not make isContext(c) true, because you then have r.PkgPath() == "golang.org/x/net/context" && r.Name() == "valueCtx".

The best way to check if a var implements an interface is to drop the reflection and use a type assertion like this:

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
}

Your code works as you expect with the function parameter because there is no underlying value, so reflect uses the interface type. But for any concrete var, it will use the actual type of the value.

答案2

得分: 1

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

type MyInterface interface {
    Greeting() string
}

在golang的实现中,eface和iface都是两个字长的结构体。eface保存数据和数据类型,iface保存数据、接口类型和数据类型。当将iface赋值给eface时,接口类型信息会被忽略,只有数据和数据类型传递给eface。

因此,reflect.TypeOf(i interface{})的参数是一个eface,没有接口类型信息(在你的情况下是context.Context)。所以你无法获取原始的接口类型。

英文:

There are two kinds of Interface in golang, aka, eface and iface. And the eface is an empty interface, which can simply represented as interface {}. The iface is kind of interface which has at least one method, such as:

type MyInterface interface {
    Greeting() string
}

In golang implementation, both eface and iface are two-word long struct. The eface holds the data and the data type, the iface holds the data, the interfacetype and the data type. When an iface assigned to an eface, the interfacetype information is ignored. Only the data and the data type passed to the eface.

So, reflect.TypeOf(i interface{}) 's parameter is and eface, no interfacetype information (aka context.Context in your case). So you can't get the original interfacetype.

huangapple
  • 本文由 发表于 2015年10月19日 00:06:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/33200095.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定