在Go中获取任何结构的大小的通用函数



我正在编写一个通用函数来获取任何类型结构的大小,类似于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

相关内容

  • 没有找到相关文章

最新更新