如何使一个 interface{} 参数指向其他内容?

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

How can I get an interface{} argument to point to something else?

问题

如何使以下代码工作并输出"Result is: [Value from GetFromMemory]."

不幸的是,我不能更改GetItemGet的方法签名。

package main

import "fmt"

type Key string

type Item struct {
    Key   Key
    Value string
}

func GetItem(key Key) interface{} {
    return &Item{key, "Value from GetFromMemory"}
}

// 如何使item指向在GetItem中创建的那个?
func Get(key Key, item interface{}) {
    item = GetItem(key)
}

func main() {
    var item Item
    Get("Key1", &item)

    // 这应该打印出"Result is: [Value from GetFromMemory]."
    fmt.Printf("Result is: [%s].", item.Value)
}

要使代码正常工作并输出期望的结果,您需要将Get函数的参数item改为指针类型。这样,您可以在函数内部修改指针指向的值。

以下是修改后的代码:

package main

import "fmt"

type Key string

type Item struct {
    Key   Key
    Value string
}

func GetItem(key Key) interface{} {
    return &Item{key, "Value from GetFromMemory"}
}

// 修改Get函数的参数为指针类型
func Get(key Key, item *interface{}) {
    *item = GetItem(key)
}

func main() {
    var item Item
    Get("Key1", &item)

    // 这应该打印出"Result is: [Value from GetFromMemory]."
    fmt.Printf("Result is: [%s].", item.Value)
}

通过将Get函数的参数item改为指针类型,并在函数内部使用解引用操作符*来修改指针指向的值,您可以使代码正常工作并输出期望的结果。

英文:

How can I make the following work and make the output "Result is: [Value from GetFromMemory]."?

Unfortunately I cannot change the method signatures of GetItem and Get.

http://play.golang.org/p/R5me3Q3y4W

package main

import "fmt"

type Key string

type Item struct {
	Key   Key
	Value string
}

func GetItem(key Key) interface{} {
	return &Item{key, "Value from GetFromMemory"}
}

// How can I make item point to the one created in GetItem?
func Get(key Key, item interface{}) {
	item = GetItem(key)
}

func main() {
	var item Item
	Get("Key1", &item)

	// This should print "Result is: [Value from GetFromMemory]."
	fmt.Printf("Result is: [%s].", item.Value)
}

答案1

得分: 9

由于您正在处理interface{}值,您可以使用类型断言或反射。

如果您知道要处理的类型,那么类型断言可能是最好的选择(在playground上的代码):

func GetItem(key Key) interface{} {
    return &Item{key, "Value from GetFromMemory"}
}

func Get(key Key, item interface{}) {
    switch v := item.(type) {
        case **Item:
            *v = GetItem(key).(*Item)
    }
}

// 使用方法:
var item *Item
Get("Key1", &item)

Get函数的代码布局使您可以轻松添加更多条件来处理其他类型。类型开关检查item的底层类型。在这种情况下,它是指向指向Item的指针(在主函数中是*Item,然后我们给Get函数传递了&item的地址,使其成为**Item)。

在与类型匹配的部分中,我们可以调用GetItem函数,断言结果对象的类型为*Item,并将其复制给*v

请注意,我将item变量更改为*Item,因为您在GetItem中生成了一个指针值,因此获取指针而不是Item对象的副本更有意义。

还请注意,您需要检查类型断言的结果,例如从GetItem中检索值的断言。如果您不这样做,而类型不匹配,比如不是*Item,您的代码将在运行时发生恐慌。

已检查的类型断言:

v, ok := someInterfaceValue.(SomeType)
// 如果断言成功,ok将为true

为了完整起见,您也可以使用反射来解决您的问题。将Get定义如下(在playground上的示例):

func Get(key Key, item interface{}) {
    itemp := reflect.ValueOf(item).Elem()
    itemp.Set(reflect.ValueOf(GetItem(key)))
}

首先,反射获取item的值(类型为**Item),并对其进行解引用,假设它是一个指针值,从而得到一个类型为*Item的反射值。然后,使用Set方法将该反射值设置为GetItem的反射值。

当然,您需要检查item的类型是否实际上是指针。如果不这样做,并且将非指针值传递给Get,将导致恐慌。

英文:

As you're dealing with interface{} values you either need type assertions or reflection.

If you know which types you will deal with, type assertions are probably the way to go
(Code on play):

func GetItem(key Key) interface{} {
    return &Item{key, "Value from GetFromMemory"}
}

func Get(key Key, item interface{}) {
	switch v := item.(type) {
		case **Item:
			*v = GetItem(key).(*Item)
	}
}

// Usage:
var item *Item
Get("Key1", &item)

The code in Get is laid out so that you can easily add more conditions for several more
types. The type switch checks the underlying type of item. In this case it is a
pointer to a pointer to an Item (it was *Item in main, then we gave Get the address of &item, making it a **Item).

In the section that matches when the type matches, we can then call GetItem, assert that the resulted object is of type *Item and copy it to *v.

Note that I changed the item variable to *Item as you're producing a pointer value in GetItem, so it makes more sense to get the pointer instead of a copy of an Item object.

Also note that you need to check the result of type assertions like the one used to
retrieve the value from GetItem. If you don't and the type does not match, say, *Item,
your code will blow up with a runtime panic.

Checked type assertions:

v, ok := someInterfaceValue.(SomeType)
// ok will be true if the assertion succeeded

For the sake of completeness, you can solve your problem with reflection as well.
Define Get as follows (Example on play):

func Get(key Key, item interface{}) {
	itemp := reflect.ValueOf(item).Elem()
	itemp.Set(reflect.ValueOf(GetItem(key)))
}

What happens is that at first the reflected value of item (type **Item) is dereferenced,
assuming that it is a pointer value, giving us a reflected value with type *Item. Said
value is then set with the reflected value of GetItem by using the Set method.

Of course you will need to check whether the kind of item is actually a pointer.
Not doing this and passing a non-pointer value to Get will result in panics.

huangapple
  • 本文由 发表于 2013年10月20日 08:09:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/19472506.html
匿名

发表评论

匿名网友

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

确定