go: 在proto中使用oneof类型

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

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

TestPB中引用了value,类型为Val

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类型赋值:

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

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

	msg.Value = &message.Val{
		OneofVal: &message.Val_Float{Float: 123},
	}

取值

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

	fv := msg.GetValue().GetStr()
	fmt.Printf("fv:%v\n", fv) // 输出 hello

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

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

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

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

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

posted @ 2023-10-11 19:57  superpigwin  阅读(176)  评论(0编辑  收藏  举报