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
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.
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.