go: 在proto中使用oneof类型

2023-10-19 14:39:53 浏览数 (2)

在proto中,可以使用OneOf类型,使用一个字段存储不同类型的数据。类似go中的interface。

假设有proto如下,Val是一个OneOf数据类型,它可以为double/int/str...中的任意一种。

TestPB中引用了value,类型为Val

代码语言:javascript复制
syntax = "proto3";

package protooneof;
option go_package = "pb/message";

message Val{
    oneof oneof_val {
      double double = 1;
      int64 int = 2;
      string str = 3;
      bytes bytes = 4;
      uint64 uint = 5;
      float float = 6;
    }
}

message TestPB {
    Val value = 1;
}

赋值

编译这个proto,可以使用以下的代码对Val类型赋值:

代码语言:javascript复制
    msg := &message.TestPB{
        Value: &message.Val{
        OneofVal: &message.Val_Str{Str: "hello"}, // 对oneof赋值需要使用Val包装,然后内部指定具体的类型
        },
    }

同理,使用=赋值也是可以的:

代码语言:javascript复制
    msg.Value = &message.Val{
        OneofVal: &message.Val_Float{Float: 123},
    }

取值

假设知道值的类型,可以直接调用GetXXX方法取值。

代码语言:javascript复制
    fv := msg.GetValue().GetStr()
    fmt.Printf("fv:%vn", fv) // 输出 hello

但是如果这个值未设置或者不存在,则会返回这个类型的0值

这就带来问题,如何判定这个值是否已设置,如果已设置是什么类型?

可以使用switch类型判定的方式来解决需求,因为已经穷举了Val所有可能的类型,如果未设置值,会打印未设置值

代码语言:javascript复制
    switch v := msg.GetValue().GetOneofVal().(type) {
    case *message.Val_Int:
        fmt.Printf("int val:%vn", v.Int)
    case *message.Val_Float:
        fmt.Printf("float val:%vn", v.Float)
    case *message.Val_Double:
        fmt.Printf("double val:%vn", v.Double)
    case *message.Val_Str:
        fmt.Printf("str val:%vn", v.Str)
    case *message.Val_Bytes:
        fmt.Printf("bytes val:%vn", v.Bytes)
    case *message.Val_Uint:
        fmt.Printf("uint val:%vn", v.Uint)
    default:
        fmt.Printf("未设置值n")
    }

通过善用Oneof类型,可以让pb的结构更紧凑,满足更多元化的需求。

0 人点赞