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> { ["last_modified"] = DateTime.UtcNow.ToString() }; person.Attributes.Add(attributes);