我正在编写一个通用函数来获取任何类型结构的大小,类似于c中的sizeof
函数。
我正试图使用接口和反射来做到这一点,但我无法得到正确的结果。代码如下:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
type myType struct {
a int
b int64
c float32
d float64
e float64
}
info := myType{1, 2, 3.0, 4.0, 5.0}
getSize(info)
}
func getSize(T interface{}) {
v := reflect.ValueOf(T)
const size = unsafe.Sizeof(v)
fmt.Println(size)
}
这段代码返回错误的结果12。
您正在获得reflect.Value
结构体的大小,而不是接口T
中包含的对象的大小。幸运的是,reflect.Type
有一个Size()
方法:
size := reflect.TypeOf(T).Size()
这给我40,这是有意义的,因为填充。
Go 1.18
在Go 1.18中,你可以使用unsafe.Sizeof
的泛型函数:
func getSize[T any]() uintptr {
var v T
return unsafe.Sizeof(v)
}
请注意,这将比使用reflect
性能更高,但它将在代码库中引入unsafe
-一些静态分析工具可能会对此发出警告。
但是,如果您的目标是提高代码重用或在运行时获取大小(请继续阅读解决方案),这不会有太大帮助,因为您仍然需要使用适当的实例化来调用函数:
type myType struct {
a int
b int64
c float32
d float64
e float64
}
func main() {
fmt.Println(getSize[myType]())
}
当作为其他泛型代码的一部分使用时,您可能会得到最大的利用,例如,在泛型类型或函数中,您将类型参数传递给getSize
。虽然如果您有参数v
,这相当于直接调用unsafe.Sizeof(v)
。使用函数隐藏unsafe
的使用仍然是有用的。一个简单的例子:
func printSize[T any](v T) {
// (doing something with v)
// instantiate with T and call
s := getSize[T]()
// s := unsafe.Sizeof(v)
fmt.Println(s)
}
否则你可以传递一个实际的参数给getSize
。然后,类型推断将使指定类型参数变得不必要。这段代码可能更灵活,允许您在运行时传递任意参数,同时保留避免反射的好处:
func getSize[T any](v T) uintptr {
return unsafe.Sizeof(v)
}
func main() {
type myType struct {
a int
b int64
c float32
d float64
e float64
}
info := myType{1, 2, 3.0, 4.0, 5.0}
// inferred type params
fmt.Println(getSize(info)) // 40
fmt.Println(getSize(5.0)) // 8
fmt.Println(getSize([]string{})) // 24
fmt.Println(getSize(struct {
id uint64
s *string
}{})) // 16
}
游乐场:https://go.dev/play/p/kfhqYHUwB2S