在Go语言中,你可以如何将接口指针转换为结构体指针?

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

In go (golang), how can you cast an interface pointer into a struct pointer?

问题

我想使用一些需要指向结构体的外部代码。在调用代码的时候,我有一个接口变量。

当从该变量创建指针时,指针的类型是 interface{}*,而我需要它是结构体类型的指针类型。

假设在 TestCanGetStructPointer 中的代码不知道 Cat 类,它存在于某个外部包中。

我该如何将其转换为所需的类型?

以下是一个代码示例:

import (
	"reflect"
	"testing"
)

type Cat struct {
	name string
}

type SomethingGeneric struct {
	getSomething func() interface{}
}

func getSomeCat() interface{} {
	return Cat{}
}

var somethingForCats = SomethingGeneric{getSomething: getSomeCat}

func TestCanGetStructPointer(t *testing.T) {
	interfaceVariable := somethingForCats.getSomething()

	pointer := &interfaceVariable

	interfaceVarType := reflect.TypeOf(interfaceVariable)
	structPointerType := reflect.PtrTo(interfaceVarType)
	pointerType := reflect.TypeOf(pointer)

	if pointerType != structPointerType {
		t.Errorf("Pointer type was %v but expected %v", pointerType, structPointerType)
	}

}

测试失败,错误信息为:

Pointer type was *interface {} but expected *parameterized.Cat

英文:

I want to use some external code that requires a pointer to a struct. At the point that the code is called, I have an interface variable.

When creating a pointer off of that variable, the pointer's type is interface{}* when I need it to be the pointer type of the struct's type.

Image the code in TestCanGetStructPointer does not know about the Cat class, and that it exists in some external package.

How can I cast it to this?

Here is a code sample:

import (
	"reflect"
	"testing"
)   

type Cat struct {
	name string
}

type SomethingGeneric struct {
	getSomething func() interface{}
}

func getSomeCat() interface{} {
	return Cat{}
}

var somethingForCats = SomethingGeneric{getSomething: getSomeCat}

func TestCanGetStructPointer(t *testing.T) {
	interfaceVariable := somethingForCats.getSomething()

	pointer := &interfaceVariable

	interfaceVarType := reflect.TypeOf(interfaceVariable)
	structPointerType := reflect.PtrTo(interfaceVarType)
	pointerType := reflect.TypeOf(pointer)

	if pointerType != structPointerType {
		t.Errorf("Pointer type was %v but expected %v", pointerType, structPointerType)
	}

}

The test fails with:

Pointer type was *interface {} but expected *parameterized.Cat

答案1

得分: 3

@dyoo的示例确实有效,但它依赖于您手动转换DogCat

这是一个有点复杂/冗长的示例,可以在一定程度上避免这种约束:

package main

import (
	"fmt"
	"reflect"
)

type Cat struct {
	name string
}

type SomethingGeneric struct {
	getSomething func() interface{}
}

func getSomeCat() interface{} {
	return Cat{name: "Fuzzy Wuzzy"}
}

var somethingForCats = SomethingGeneric{getSomething: getSomeCat}

func main() {
	interfaceVariable := somethingForCats.getSomething()
	castVar := reflect.ValueOf(interfaceVariable)
	castVar.Convert(castVar.Type())

	// 如果你想要一个指针,可以这样做:
	fmt.Println(reflect.PtrTo(castVar.Type()))

	// 解引用后的值
	if castVar.Type() != reflect.TypeOf(Cat{}) {
		fmt.Printf("类型为 %v,但期望的类型为 %v\n", castVar, reflect.TypeOf(&Cat{}))
	} else {
		fmt.Println(castVar.Field(0))
	}
}

Playground链接

英文:

@dyoo's example does work, but it relies on you to manually cast Dog and Cat.

Here's a bit of a convoluted/verbose example which avoids that constraint somewhat:

package main

import (
	"fmt"
	"reflect"
)

type Cat struct {
	name string
}

type SomethingGeneric struct {
	getSomething func() interface{}
}

func getSomeCat() interface{} {
	return Cat{name: "Fuzzy Wuzzy"}
}

var somethingForCats = SomethingGeneric{getSomething: getSomeCat}

func main() {
	interfaceVariable := somethingForCats.getSomething()
	castVar := reflect.ValueOf(interfaceVariable)
	castVar.Convert(castVar.Type())

    // If you want a pointer, do this:
	fmt.Println(reflect.PtrTo(castVar.Type()))

    // The deref'd val
	if castVar.Type() != reflect.TypeOf(Cat{}) {
		fmt.Printf("Type was %v but expected %v\n", castVar, reflect.TypeOf(&Cat{}))
	} else {
		fmt.Println(castVar.Field(0))
	}
}

Playground Link

答案2

得分: 3

我找到了这个帖子:https://groups.google.com/forum/#!topic/golang-nuts/KB3_Yj3Ny4c

package main

import (
	"fmt"
	"reflect"
)

type Cat struct {
	name string
}

//
// Return a pointer to the supplied struct via interface{}
//
func to_struct_ptr(obj interface{}) interface{} {

	fmt.Println("obj is a", reflect.TypeOf(obj).Name())

	// Create a new instance of the underlying type	
	vp := reflect.New(reflect.TypeOf(obj))

	// Should be a *Cat and Cat respectively
	fmt.Println("vp is", vp.Type(), " to a ", vp.Elem().Type())

	vp.Elem().Set(reflect.ValueOf(obj))

	// NOTE: `vp.Elem().Set(reflect.ValueOf(&obj).Elem())` does not work

	// Return a `Cat` pointer to obj -- i.e. &obj.(*Cat)
	return vp.Interface()
}

//
// Dump out a pointer ...
//
func test_ptr(ptr interface{}) {
	v := reflect.ValueOf(ptr)
	fmt.Println("ptr is a", v.Type(), "to a", reflect.Indirect(v).Type())
}

func main() {
	cat := Cat{name: "Fuzzy Wuzzy"}

	// Reports "*main.Cat"
	test_ptr(&cat)

	// Get a "*Cat" generically via interface{}
	sp := to_struct_ptr(cat)

	// *should* report "*main.Cat" also
	test_ptr(sp)

	fmt.Println("sp is", sp)
}

以上是要翻译的内容。

英文:

I found this thread: https://groups.google.com/forum/#!topic/golang-nuts/KB3_Yj3Ny4c

package main

import (
	"fmt"
	"reflect"
)

type Cat struct {
	name string
}

//
// Return a pointer to the supplied struct via interface{}
//
func to_struct_ptr(obj interface{}) interface{} {

	fmt.Println("obj is a", reflect.TypeOf(obj).Name())
	
	// Create a new instance of the underlying type	
	vp := reflect.New(reflect.TypeOf(obj))

	// Should be a *Cat and Cat respectively
	fmt.Println("vp is", vp.Type(), " to a ", vp.Elem().Type())

	vp.Elem().Set(reflect.ValueOf(obj))
	
	// NOTE: `vp.Elem().Set(reflect.ValueOf(&obj).Elem())` does not work
		
	// Return a `Cat` pointer to obj -- i.e. &obj.(*Cat)
	return vp.Interface()
}

//
// Dump out a pointer ...
//
func test_ptr(ptr interface{}) {
	v := reflect.ValueOf(ptr)
	fmt.Println("ptr is a", v.Type(), "to a", reflect.Indirect(v).Type())
}

func main() {
	cat := Cat{name: "Fuzzy Wuzzy"}

	// Reports "*main.Cat"
	test_ptr(&cat)
	
	// Get a "*Cat" generically via interface{}
	sp := to_struct_ptr(cat)
	
	// *should* report "*main.Cat" also
	test_ptr(sp)
	
	fmt.Println("sp is",sp)
}

答案3

得分: 2

以下是翻译好的内容:

以下可能会有所帮助:http://play.golang.org/p/XkdzeizPpP

package main

import (
	"fmt"
)

type Cat struct {
	name string
}

type Dog struct {
	name string
}

type SomethingGeneric struct {
	getSomething func() interface{}
}

func getSomeCat() interface{} {
	return Cat{name: "加菲猫"}
}

func getSomeDog() interface{} {
	return Dog{name: "菲多狗"}
}

var somethings = []SomethingGeneric{
	SomethingGeneric{getSomething: getSomeCat},
	SomethingGeneric{getSomething: getSomeDog},
}

func main() {
	for _, something := range somethings {
		interfaceVariable := something.getSomething()

		cat, isCat := interfaceVariable.(Cat)
		dog, isDog := interfaceVariable.(Dog)

		fmt.Printf("猫 %v %v\n", cat, isCat)
		fmt.Printf("狗 %v %v\n", dog, isDog)
	}
}
英文:

The following may help: http://play.golang.org/p/XkdzeizPpP

package main

import (
	"fmt"
)

type Cat struct {
	name string
}

type Dog struct {
	name string
}

type SomethingGeneric struct {
	getSomething func() interface{}
}

func getSomeCat() interface{} {
	return Cat{name: "garfield"}
}

func getSomeDog() interface{} {
	return Dog{name: "fido"}
}

var somethings = []SomethingGeneric{
	SomethingGeneric{getSomething: getSomeCat},
	SomethingGeneric{getSomething: getSomeDog},
}

func main() {
	for _, something := range somethings {
		interfaceVariable := something.getSomething()

		cat, isCat := interfaceVariable.(Cat)
		dog, isDog := interfaceVariable.(Dog)

		fmt.Printf("cat %v %v\n", cat, isCat)
		fmt.Printf("dog %v %v\n", dog, isDog)
	}
}

huangapple
  • 本文由 发表于 2015年3月24日 06:47:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/29221854.html
匿名

发表评论

匿名网友

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

确定