您可以使用以下方式模拟 AppEngine 数据存储界面reflect http://golang.org/pkg/reflect/;通常我会说最小化反射,但是您(以及 AppEngine 和其他 ORM)在这里没有其他很好的选择来呈现您想要的界面。对于模仿的东西Get
you:
- get a
reflect.Value
with ValueOf()
- 获取您想要创建的事物的类型
- 创建它与
reflect.Zero
- 可选地填写一些数据
reflect.Field()
, etc.
- use
reflect.Indirect()
and Value.Set()
通过指针设置原稿。
一个简单的例子,只是通过指针将结构清零,位于http://play.golang.org/p/g7dNlrG_vr 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 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))
}
}
在实时代码中(与您正在进行的测试相反),使用类型开关(如 Martin 建议的那样)会更快,以便为每种类型运行专门的本机代码;如果您按类型有不同的行为,这也可能很方便。一个例子GetMulti
is at http://play.golang.org/p/q-9WyUqv6P 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 http://play.golang.org/p/6qw52B7eC3(不是复制,因为它是上述两者的简单拼接)。
恰巧有最近的另一个问题 https://stackoverflow.com/questions/26764907/why-cant-i-use-reflection-to-take-the-address-of-a-slice/26765210#26765210关于如何传递一个值GetMulti
,而不是模仿GetMulti
本身,如果出现的话。
更多的是作为一般参考而不是回答这个问题:
了解“Go 缺乏引用传递”很有用,但也需要一些阐述。 Go 有指针,以及其他类型,例如包含数据指针的切片。不存在“按引用传递”的含义只是 Go 永远不会更改值参数(int
, struct
) 隐式转换为指针。 C++ 引用参数正是这样做的:C++void f(i int&) { i++; }
变化i
在调用者中,无需调用者在调用点显式传递指针。func (i int) { i++ }
没有。
在 Go 中,您可以查看传递给函数调用的类型并了解它可以更改哪些数据。使用 C++ 引用参数或某些语言的“按引用传递”语义,any通话可能会改变当地人;如果不查看声明,你就无法判断。
为了避免不必要的数据复制,切片、字符串、映射、接口和通道值的实现中已经存在指针。在这些类型中,指针、切片和映射实际上允许您通过它们修改数据。此外,与 C++ 一样,Go 的this
-like 接收者参数可以是一个没有显式的指针&
在调用代码中。有更多关于此的内容Russ Cox 的 godata 帖子 http://research.swtch.com/godata and 关于何时需要指针的总结 https://stackoverflow.com/questions/23542989/pointers-vs-values-in-parameters-and-return-values/23551970.