2014-11-01 40 views
4

我很难理解如何设置已经作为指针传递的接口值。我试图完成沿此线的东西:设置接口引用{}参数

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

什么会我必须做我的程序输出是

num: 9001 

编辑:有没有使用reflect可能包罗万象的解决方案?

+1

只是猜测,但也许你正试图从appengine数据存储'Get'。通过指针(GetMulti,指向片的指针)返回是奇怪的鸭子;它似乎也许是他们试图在实现中保持混乱,而不是返回一个'interface {}'并让调用者转换。要做这样的事情,你将不得不使用'反射',这是没有趣。如果你想处理特定的切片类型(专用或作为几种类型的“快速路径”),请使用类似于PeterSO所说的类型断言(或几种类型的开关)。 – twotwotwo 2014-11-02 06:32:58

+0

你可能看过一篇名为“反思法则”的博客文章。第一条反思原则应该避免反思。 :)但是,无论在这里还是在任何地方,您的代码都需要阅读或构建任意类型的结构。 – twotwotwo 2014-11-02 06:42:16

+1

不错的一个。它是appengine数据存储区。我完全应该这样说,但我正在编写一个测试实现,它基本上是数据存储的紧凑包装。 – BlinkyTop 2014-11-02 15:49:02

回答

6

可以效仿使用reflect的应用服务引擎的数据存储接口;通常我会说尽量减少反射,但是你(和AppEngine和其他ORMs)在这里没有其他很好的选择来呈现你想要的接口。对于一些模拟Get你:

  • 得到reflect.ValueValueOf()
  • 得到您想要创建的东西类型
  • reflect.Zero
  • 创建它在一些数据可选填充reflect.Field()等。
  • 使用reflect.Indirect()Value.Set()通过指针设置原件。

,仅仅通过指针归零一个struct一个简单的例子是在http://play.golang.org/p/g7dNlrG_vr和复制在这里:

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

对于模拟GetMulti你需要更多的步骤,切片工作。一个例子是在http://play.golang.org/p/G_6jit2t-2及以下:

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

在现场代码(而不是测试像你这样做),这将会是更快地使用一种类型的开关(如马丁建议),因此专门的本地代码针对每种类型运行;如果按类型有不同的行为,这也可能很方便。为GetMulti一个例子是在http://play.golang.org/p/q-9WyUqv6P及以下:

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

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

碰巧有another recent question关于如何将值传递给GetMulti,而不是模拟GetMulti本身,如果出现。


更一般参考,而不是回答这个问题:

“围棋缺乏按引用传递”是需要了解的,还需要一些阐述。 Go有指针和其他类型的切片,包含指向数据的指针。没有“通过引用传递”的意思就是Go永远不会将一个值参数(int,struct)隐式地转换为指针。 C++引用参数的确如此:C++ void f(i int&) { i++; }在调用方中更改了i,而调用方没有明确地在调用点处传递指针。 func (i int) { i++ }没有。

在Go中,您可以查看传递给函数调用的类型并告诉它可以更改哪些数据。使用C++引用参数或某些语言的“通过引用传递”语义,任何调用都可能会改变本地语言;没有查看声明就无法说清楚。为了避免不必要的数据复制,在slice,string,map,interface和channel values的实现中已经有了指针。在这些类型中,指针,切片和地图实际上会让您通过它们修改数据。此外,与在C++中一样,Go的this类接收器参数可以是调用代码中没有明确的&的指针。有更多关于这个在Russ Cox's godata postthis summary on when you need a pointer or not

+0

刚刚意识到您可能正在执行'Get',并且这里只讨论了'GetMulti',所以我添加了一个没有额外片段相关位的示例。 – twotwotwo 2014-11-09 05:56:03

1

首先:有绝对在Go中没有“通过引用”的概念。没有。你什么可以是绕过一个指针。这个指针值是按值传递的,因为Go中的所有东西都是按值传递的。第二:不要传入一个指向接口的指针,并修改pointees的值(可行但丑陋),你可以返回值(更好)。

第三:它不能(即不是没有反射或不安全)没有类型断言。 而且你永远不应该(直到你掌握Go和接口为止),使用指向接口的指针。

第五:如果您的解决方案需要interface{}您可能会做错事。你确定你的实体不能被一些(非空的)接口描述吗?

这就是说,这样的工作。

func main() { 
    var num int 
    Stuff["key"] = 9001 
    num = get("key").(int) 
} 
func get(k string) interface{}{ 
    return Stuff[k] 
} 
+1

我宁愿从函数返回值,但我试图满足一个已经标准化的接口(根据应用引擎数据存储的具体情况而定) – BlinkyTop 2014-11-01 12:27:48

1

Martin Gallagher解决方案非常完美,但正如他所说的,您不能在Golang中使用泛型,因此代码看起来有点难看。我想另一种解决方案是始终使用interface{}作为类型,然后在程序中投射(或检查类型)。事情是这样的:http://play.golang.org/p/0o20jToXHV

2

The Go Programming Language Specification

Calls

在函数调用时,函数值和变量 通常的顺序进行评估。评估完成后,呼叫 的参数将按值传递给函数,被调用的函数将开始执行 。当函数返回时,函数的返回参数通过值 传回给调用函数。

在去一切都通过价值;没有任何东西通过参考传递因此,传递一个指针。例如,

package main 

import "fmt" 

var Stuff map[string]interface{} 

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

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

输出:

 
num: 9001