可以效仿使用reflect
的应用服务引擎的数据存储接口;通常我会说尽量减少反射,但是你(和AppEngine和其他ORMs)在这里没有其他很好的选择来呈现你想要的接口。对于一些模拟Get
你:
- 得到
reflect.Value
与ValueOf()
- 得到您想要创建的东西类型
- 与
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 post和this summary on when you need a pointer or not。
只是猜测,但也许你正试图从appengine数据存储'Get'。通过指针(GetMulti,指向片的指针)返回是奇怪的鸭子;它似乎也许是他们试图在实现中保持混乱,而不是返回一个'interface {}'并让调用者转换。要做这样的事情,你将不得不使用'反射',这是没有趣。如果你想处理特定的切片类型(专用或作为几种类型的“快速路径”),请使用类似于PeterSO所说的类型断言(或几种类型的开关)。 – twotwotwo 2014-11-02 06:32:58
你可能看过一篇名为“反思法则”的博客文章。第一条反思原则应该避免反思。 :)但是,无论在这里还是在任何地方,您的代码都需要阅读或构建任意类型的结构。 – twotwotwo 2014-11-02 06:42:16
不错的一个。它是appengine数据存储区。我完全应该这样说,但我正在编写一个测试实现,它基本上是数据存储的紧凑包装。 – BlinkyTop 2014-11-02 15:49:02