【问题标题】:what's the most efficient way to iterate over an interface{} object in Go?在 Go 中迭代 interface{} 对象的最有效方法是什么?
【发布时间】:2023-01-11 12:55:50
【问题描述】:

例如假设 interface{} 对象是一个{"a":1, "b": "test", c: &AnotherStruct{}}结构体,我们需要迭代这个对象来获取每个字段“a”、“b”、“c”的值。

我可以想到两种方法:

  1. 直接使用 Go 反射。
  2. 使用 json.Marshal()/json.Unmarshal() 将对象转换为 map[string]interface{},然后遍历 map 做类型断言,这也调用了反射,但是可能有一些 json具有内部优化的库可能会获得更好的性能,例如https://github.com/bytedance/sonic

    我想知道哪个更有效,还有其他方法吗?

【问题讨论】:

  • json.Marshal/Unmarshal 也是通过反射实现的。
  • 使用类型断言
  • “迭代”是什么意思?你想达到什么目的?
  • 你需要反省。
  • 回复您的编辑:reflect 是唯一的方法。你不会通过添加更多不相关/不必要的步骤来提高效率,即使图书馆可以相对快速地完成这些步骤。 json/sonic是序列化库,跟你的问题没有直接关系。

标签: go reflection


【解决方案1】:

interface{} 是(遗留)“这可能是任何东西”的缩写。它不代表一个“对象”(尽管它可以)。从 go 1.18 开始,关键字 any 被引入作为 interface{} 的直接替代品(尽管如果您需要与旧的 golang 版本兼容,可以继续使用后者)。

在这里,为了简洁起见,我将使用any

我建议忽略效率,除非/直到它成为您需要解决的问题,而不是专注于最清晰和最简单的方法来实现您需要的功能。

很难清楚地知道您究竟面临着什么以及想要实现什么;您的“示例”对象包含带引号和未带引号的字段成员,因此我看到您可能正在处理四种可能的情况:

  1. 一个any 变量保存一个已知正式结构类型的值
  2. 一个any 变量持有一个匿名正式结构类型的值
  3. any 变量保存一个 JSON 字符串
  4. 一个any变量持有一个map[string]any

    对于场景 1 和 2,将编组为 JSON,然后解组为 map[string]any

    对于场景 3,您将强制转换为字符串,然后解组为 map[string]any

    对于场景 4,您将直接转换为 map[string]any

    我已经在操场上为您解决了所有这些问题:https://go.dev/play/p/cSdUmynTFRp

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type AnotherStruct struct {
        X int `json:"x"`
    }
    
    type Foo struct {
        A int           `json:"a"`
        B string        `json:"b"`
        C AnotherStruct `json:"c"`
    }
    
    func emit(m map[string]any) {
        for k, v := range m {
            fmt.Printf("  %s: %s
    ", k, v)
        }
        fmt.Println()
    }
    
    func scenario1or2(n int, foo any) map[string]any {
        fmt.Printf("scenario %d:
    ", n)
    
        j, _ := json.Marshal(foo)
    
        m := map[string]any{}
        json.Unmarshal(j, &m)
    
        return m
    }
    
    func scenario3(foo any) map[string]any {
        fmt.Println("scenario 3")
    
        m := map[string]any{}
        json.Unmarshal([]byte(foo.(string)), &m)
    
        return m
    }
    
    func scenario4(foo any) map[string]any {
        fmt.Println("scenario 4")
    
        return foo.(map[string]any)
    }
    
    func main() {
        emit(scenario1or2(1, Foo{
            A: 1,
            B: "test",
            C: AnotherStruct{X: 42},
        }))
    
        emit(scenario1or2(2, struct {
            A int
            B string
            C AnotherStruct
        }{
            A: 1,
            B: "test",
            C: AnotherStruct{
                X: 42,
            },
        }))
    
        emit(scenario3(`{"a":1,"b":"test","c":{"x":42}}`))
    
        emit(scenario4(map[string]any{
            "a": 1,
            "b": "test",
            "c": AnotherStruct{
                X: 42,
            },
        }))
    }
    

    如果您有场景 1 并且只是想有效地访问字段(即潜在地迭代未知fields 实际上不是必需的)那么你可以直接类型转换为已知的正式结构类型:

       // assuming...
       type Foo struct {
         a int
         b string
         c *AnotherStruct {
         }
       }
    
       // and where 'anyfoo' is an `any` holding a Foo
       foo := anyfoo.(Foo)
    
       a := foo.a
       b := foo.b
       c := foo.c
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-21
    • 2019-08-31
    • 2022-10-19
    • 2020-07-18
    • 2016-05-25
    • 2012-03-03
    • 2023-03-23
    相关资源
    最近更新 更多