通过引用设置接口{}参数 [英] Setting An Interface{} Parameter By Reference

查看:102
本文介绍了通过引用设置接口{}参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难理解如何设置已经作为指针传递的接口值。我试图完成以下内容:

  importfmt

var Stuff map [string] interface {}

func main(){
var num int
Stuff [key] = 9001
get(key,& ; num)
fmt.Println(num:,num)
}

func get(k string,v interface {}){
* v = Stuff [k]
}

我需要做什么才能使我的程序输出成为

 编号:9001 

编辑:是否有可能使用反映?的解决方案?

解决方案

您可以使用 reflect 模拟AppEngine数据存储接口$ C> ;通常我会说尽量减少反射,但是你(和AppEngine和其他ORMs)在这里没有其他很好的选择来呈现你想要的接口。对于模拟获取您的内容:




  • 获取反映.Value ValueOf()

  • 获取要创建的东西的类型
  • >
  • 使用 reflect.Zero
  • 创建它
  • 可选地使用填充某些数据> reflect.Field()等。

  • 使用 reflect.Indirect() Value.Set()通过指针设置原始数据。


一个简单的例子,只是通过指针清零结构位于 http://play.golang.org/p/g7dNlrG_vr 并在这里复制:

 包主
$ b导入(
fmt
反映


func main(){
i:= 1
clear(& i)
fmt.Println(i)
}

func clear(dst interface {}){
// ValueOf输入反射地点
dstPtrValue:= reflect.ValueOf(dst)
//需要类型来创建一个值
dstPtrType:= dstPtrValue.Type()
// * T - > T,崩溃如果不是ptr
dstType:= dstPtrType.Elem()
// * dst在* dst =零
dstValue:= reflect.Indirect(dstPtrValue)
// zero中的* dst =零
zeroValue:= reflect.Zero(dstType)
// the = in * dst = 0
dstValue.Set(zeroValue)
}

为了模拟 GetMulti ,您需要更多步骤与切片一起工作。例如 http://play.golang.org/p/G_6jit2t-2 及以下:

 包主

导入(
fmt
反映


func main(){
s:= [] int {}
getMultiZeroes(& s,10)
fmt。 println(s)
}

func getMultiZeroes(slicePtrIface接口{},howMany int){
//输入`reflect` -land
slicePtrValue:= reflect。 ValueOf(slicePtrIface)
//获取类型
slicePtrType:= slicePtrValue.Type()
//从`* [] T`导航到`T`
sliceElemType:=如果输入类型不是`* [] T`
// //我们需要这个将Append()添加到
sliceValue:= reflect.Indirect(slicePtrValue )
//这和Append()
sliceElemValue:= reflect.Zero(sliceElemType)

//追加请求的零数
for i:= 0 ;我<多少; i ++ {
// s:= append(s,v)
sliceValue.Set(reflect.Append(sliceValue,sliceElemValue))
}
}

在实时代码中(与您正在进行的测试相反),使用类型开关会更快马丁建议),以便为每种类型运行专门的本机代码;如果按类型有不同的行为,这也可能很方便。 GetMulti 的示例位于 http:// play.golang.org/p/q-9WyUqv6P 及以下:

 包主

$ f
$ b $ func main(){
s:= [] int {}
getZeroes(& s)
fmt.Println(s)
$ b失败:= [] float32 {}
getZeroes(& failed)
}

func getZeroes(slicePtrIface接口{}){
$ case * [] int:
(* sp)= append((* sp),0,0)
case * [] $ b $ switch(sp)= slicePtrIface。字符串:
(* sp)= append((* sp),,)
默认值:
panic(fmt.Sprintf(getZeroes:传入类型%T,不是指向受支持类型片的指针,slicePtrIface))
}
}

你甚至可以把这两者结合起来。编写常见类型的自定义代码并在默认情况下调用缓慢的反射版本。在 http://play.golang.org/p/6qw52B7eC3 上演示(不要复制,因为它是一个这样一个简单的拼接在一起的上述两个)。



碰巧有另一个最近的问题如何让一个值传递给 GetMulti ,而不是模拟 GetMulti 本身,如果出现的话。






更多的一般参考资料可以回答这个问题:

Go缺乏传递参考对于了解有用,但也需要一些细节。 Go有指针和其他类型的切片,包含指向数据的指针。没有通过引用传递的含义就是Go永远不会改变一个值参数( int struct )隐式指向一个指针。 C ++引用参数的确如此:C ++ void f(int&){i ++; } 改变 i 在调用者中没有调用者明确地传入一个指向调用点的指针。 func(i int){i ++} 不。

在Go中,您可以查看传递给函数调用的类型,并告诉它可以更改哪些数据。使用C ++引用参数或某些语言的按引用传递语义时,任何调用都可能会改变locals;没有查看声明就无法说清楚。为避免不必要的数据复制,已经有指针在slice,string,map,interface和channel values的实现中。在这些类型中,指针,切片和地图实际上会让您通过它们修改数据。另外,像C ++一样,Go的这个类型的接收者参数可以是一个指针,在调用时不需要显式的& 码。 Russ Cox的godata帖子这个摘要是关于什么时候需要一个指针

I am having difficulty understanding how to set an interface value that has been passed as a pointer. I am trying to accomplish something along the lines of this:

import "fmt"

var Stuff map[string]interface{}

func main() {
    var num int
    Stuff["key"] = 9001
    get("key", &num)
    fmt.Println("num:", num)
}

func get(k string, v interface{}) {
    *v = Stuff[k]
}

What would I have to do to make my program output be

num: 9001

Edit: is there a possible catch-all solution using reflect?

解决方案

You can emulate the AppEngine datastore interface using reflect; usually I say minimize reflection, but you (and AppEngine and other ORMs) have no other great option here to present the interface you want. For something emulating Get you:

  • get a reflect.Value with ValueOf()
  • get the type of the thing you want to create
  • create it with reflect.Zero
  • optionally fill in some data with reflect.Field(), etc.
  • use reflect.Indirect() and Value.Set() to set the original through the pointer.

A trivial example that just zeroes a struct through a pointer is at http://play.golang.org/p/g7dNlrG_vr and copied here:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    i := 1
    clear(&i)
    fmt.Println(i)
}

func clear(dst interface{}) {
    // ValueOf to enter reflect-land
    dstPtrValue := reflect.ValueOf(dst)
    // need the type to create a value
    dstPtrType := dstPtrValue.Type()
    // *T -> T, crashes if not a ptr
    dstType := dstPtrType.Elem()
    // the *dst in *dst = zero
    dstValue := reflect.Indirect(dstPtrValue)
    // the zero in *dst = zero
    zeroValue := reflect.Zero(dstType)
    // the = in *dst = 0
    dstValue.Set(zeroValue)
}

For emulating GetMulti you need more steps to work with the slice. An example is at http://play.golang.org/p/G_6jit2t-2 and below:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    s := []int{}
    getMultiZeroes(&s, 10)
    fmt.Println(s)
}

func getMultiZeroes(slicePtrIface interface{}, howMany int) {
    // enter `reflect`-land
    slicePtrValue := reflect.ValueOf(slicePtrIface)
    // get the type
    slicePtrType := slicePtrValue.Type()
    // navigate from `*[]T` to `T`
    sliceElemType := slicePtrType.Elem().Elem() // crashes if input type not `*[]T`
    // we'll need this to Append() to
    sliceValue := reflect.Indirect(slicePtrValue)
    // and this to Append()
    sliceElemValue := reflect.Zero(sliceElemType)

    // append requested number of zeroes
    for i := 0; i < howMany; i++ {
        // s := append(s, v)
        sliceValue.Set(reflect.Append(sliceValue, sliceElemValue))
    }
}

In live code (as opposed to testing like you're doing), it'd be faster to use a type switch (as Martin suggested) so that specialized native code runs for each type; that might also be handy if you have different behavior by type. An example for GetMulti is at http://play.golang.org/p/q-9WyUqv6P and below:

package main

import "fmt"

func main() {
    s := []int{}
    getZeroes(&s)
    fmt.Println(s)

    fails := []float32{}
    getZeroes(&fails)
}

func getZeroes(slicePtrIface interface{}) {
    switch sp := slicePtrIface.(type) {
    case *[]int:
        (*sp) = append((*sp), 0, 0)
    case *[]string:
        (*sp) = append((*sp), "", "")
    default:
        panic(fmt.Sprintf("getZeroes: passed type %T, which is not a pointer to a slice of a supported type", slicePtrIface))
    }
}

You could even trivially combine the two; write custom code for common types and call the slow reflect-based version in the default case. Demo at http://play.golang.org/p/6qw52B7eC3 (not copying because it's a such a simple stitching together of the two above).

There happened to be another recent question on how to make a value to pass to GetMulti, rather than emulating the GetMulti itself, if that comes up.


More for general reference than to answer this:

"Go lacks pass by reference" is useful to know, but also needs some elaboration. Go has pointers, and other types like slices that contain pointers to data. The sense in which there isn't "pass by reference" is just that Go will never change a value argument (int, struct) into a pointer implicitly. C++ reference arguments do exactly that: C++ void f(i int&) { i++; } changes i in the caller without the caller explicitly passing in a pointer at the callsite. func (i int) { i++ } doesn't.

In Go, you can look at the types passed to a function call and tell what data it can change. With C++ reference arguments or some languages' "pass by reference" semantics, any call might change locals; you can't tell without looking up the declarations.

For purposes of avoiding unnecessary copying of data, there are already pointers in the implementations of slice, string, map, interface, and channel values. Of those types, pointers, slices, and maps will actually let you modify data through them. Also, like in C++, Go's this-like receiver parameter can be a pointer without an explicit & in the calling code. There's more about this in Russ Cox's godata post and this summary on when you need a pointer or not.

这篇关于通过引用设置接口{}参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆