可以效仿使用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