当 .NET 遇上 gRPC:Protobuf 与 C# 数据类型对应表
gRPC 使用 Protobuf 作为其接口定义语言 (IDL)。消息是 Protobuf 中的主要数据传输对象。它们在概念上类似于 .NET 类。
syntax = "proto3";option csharp_namespace = "Contoso.Messages";message Person {int32 id = 1;string first_name = 2;string last_name = 3;}
前面的消息定义将三个字段指定为名称/值对。与 .NET 类型上的属性类似,每个字段都有名称和类型。字段类型可以是 Protobuf 标量值类型(如 int32),也可以是其他消息。
Protobuf 样式指南建议使用 underscore_separated_names 作为字段名称。为 .NET 应用创建的新 Protobuf 消息应遵循 Protobuf 样式准则。.NET 工具会自动生成使用 .NET 命名标准的 .NET 类型。例如,first_name Protobuf 字段生成 FirstName .NET 属性。
值类型对应表
| .proto 类型 | C# 类型 | 备注 |
| double | double | |
| float | float | |
| int32 | int | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 |
| int64 | long | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。 |
| uint32 | uint | |
| uint64 | ulong | |
| sint32 | int | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。 |
| sint64 | long | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。 |
| fixed32 | uint | |
| fixed64 | ulong | |
| sfixed32 | int | 总是4个字节。 |
| sfixed64 | long | 总是8个字节。 |
| bool | bool | |
| string | string | |
| bytes | ByteString | 可能包含任意顺序的字节数据 |
值类型始终具有默认值,并且该默认值不能设置为 null 。此项约束包括 string 和 ByteString,他们都属于 C# 类。string 默认为空字符串,ByteString 默认为空字节值。尝试将他们设置为 null 会引发错误。
可为 null 的类型
C# 的 Protobuf 代码生成使用本地类型,如 int 表示 int32。因此这些值始终包括在内,不能为 null。
对于需要显式 null 的值(例如在 C# 代码中使用 int?),Protobuf 的“已知类型”包括编译为可以为 null 的 C# 类型的包装器。若要使用它们,请将 wrappers.proto 导入到 .proto 文件中,如以下代码所示:
syntax = "proto3"import "google/protobuf/wrappers.proto"message Person {// ...google.protobuf.Int32Value age = 5;}
wrappers.proto 类型不会在生成的属性中公开。Protobuf 会自动将它们映射到 C# 消息中相应的可为 null 的 .NET 类型。例如,google.protobuf.Int32Value 字段生成 int? 属性。引用类型属性(如 string 和 ByteString )保持不变,但可以向它们分配 null,这不会引发错误。
| C# 类型 | 类型包装器 |
|---|---|
bool? |
google.protobuf.BoolValue |
double? |
google.protobuf.DoubleValue |
float? |
google.protobuf.FloatValue |
int? |
google.protobuf.Int32Value |
long? |
google.protobuf.Int64Value |
uint? |
google.protobuf.UInt32Value |
ulong? |
google.protobuf.UInt64Value |
string |
google.protobuf.StringValue |
ByteString |
google.protobuf.BytesValue |
集合
列表
Protobuf 中,在字段上使用 repeated 前缀关键字指定列表。
message Person {// ...repeated string roles = 8;}在生成的代码中,repeated 字段由 Google.Protobuf.Collections.RepeatedField<T> 泛型类型表示。public class Person{// ...public RepeatedField<string> Roles { get; }}
RepeatedField<T> 实现了 IList<T> 接口,因此可以应用 LINQ 查询,或者将其转换为数组或列表。RepeatedField<T> 属性没有公开的设置器,所以他在内部保证了不可为空。需要向其中添加数据时调用 Add 方法即可:
var person = new Person();// Add one item.person.Roles.Add("user");// Add all items from another collection.var roles = new [] { "admin", "manager" };person.Roles.Add(roles);
字典
.NET IDictionary<TKey,TValue> 类型在 Protobuf 中使用 map 表示。
message Person {// ...map<string, string> attributes = 9;}
在生成的 .NET 代码中,map 字段由 Google.Protobuf.Collections.MapField<TKey, TValue> 泛型类型表示。MapField <TKey, TValue> 实现了 IDictionary <TKey, TValue> 接口。与 repeated 属性一样,map 属性没有公开的设置器 。
var person = new Person();// Add one item.person.Attributes["created_by"] = "James";// Add all items from another collection.var attributes = new Dictionary<string, string>{[] = DateTime.UtcNow.ToString()};person.Attributes.Add(attributes);
