当 .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);