当你处理有符号的值时,
int32
和
int64
的标准编码效率很低。 如果你的字段可能包含负数,请改用
sint32
或
sint64
。 这些类型分别映射到 C# 的
int
和
long
类型。
无论值是多少,
fixed
字段始终使用相同的字节数。 此行为可以更快地对较大的值进行序列化和反序列化。
Protobuf 字符串是 UTF-8(或 7 位数 ASCII)编码的。 编码长度不能大于 2
32
。
Protobuf 运行时提供与 C#
byte[]
数组轻松映射的
ByteString
类型。
其他 .NET 基元类型
日期和时间
本机标量类型不提供与 C# 的
DateTimeOffset
、
DateTime
和
TimeSpan
等效的日期和时间值。 你可以通过使用 Google 的一些“已知类型”扩展来指定这些类型。 这些扩展为受支持平台中的复杂字段类型提供代码生成和运行时支持。
下表显示日期和时间类型:
C# 类型
Protobuf 已知类型
string subject = 1;
google.protobuf.Timestamp time = 2;
google.protobuf.Duration duration = 3;
C# 类中生成的属性不是 .NET 日期和时间类型。 属性使用
Google.Protobuf.WellKnownTypes
命名空间中的
Timestamp
和
Duration
类。 这些类提供在
DateTimeOffset
、
DateTime
和
TimeSpan
之间进行转换的方法。
// Create Timestamp and Duration from .NET DateTimeOffset and TimeSpan
var meeting = new Meeting
Time = Timestamp.FromDateTimeOffset(meetingTime), // also FromDateTime()
Duration = Duration.FromTimeSpan(meetingLength)
// Convert Timestamp and Duration to .NET DateTimeOffset and TimeSpan
DateTimeOffset time = meeting.Time.ToDateTimeOffset();
TimeSpan? duration = meeting.Duration?.ToTimeSpan();
Timestamp
类型适用于 UTC 时间。 DateTimeOffset
值的偏移量始终为零,并且 DateTime.Kind
属性始终为 DateTimeKind.Utc
。
System.Guid
Protobuf 不直接支持 Guid 类型,(在其他平台上称为 UUID
类型)。 它没有适用的已知类型。
最佳方法是使用标准 8-4-4-4-12
十六进制格式(例如,45a9fda3-bd01-47a9-8460-c1cd7484b0b3
)将 Guid
值作为 string
字段处理。 所有语言和平台都可以分析该格式。
不要为 Guid
值使用 bytes
字段。 当 Protobuf 与其他平台(如 Java)交互时,字节序(维基百科的定义)的问题会导致行为不稳定。
可为 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;
对于生成的消息属性,Protobuf 将使用简单的 T?
,例如 int?
。
下表完整列出了包装器类型以及它们的等效 C# 类型:
C# 类型
已知类型包装器
已知类型 Timestamp
和 Duration
以 .NET 形式表示为类。 在 C# 8 和更高版本中,可以使用可以为 null 的引用类型。 但在转换为 DateTimeOffset
或 TimeSpan
时,请务必检查这些类型的属性是否为 null。
Protobuf 本身不支持 .NET decimal
类型,只支持 double
和 float
。 在 Protobuf 项目中,我们正在探讨这样一种可能性:将标准 Decimal
类型添加到已知类型,并为支持它的语言和框架添加平台支持。 尚未实现任何内容。
可以创建消息定义来表示 decimal
类型,以便在 .NET 客户端和服务器之间实现安全序列化。 但其他平台上的开发人员必须了解所使用的格式,并能够实现自己对其的处理。
为 Protobuf 创建自定义 decimal 类型
简单实现可能与某些 Google API 使用的非标准 Money
类型相似,无需 currency
字段。
package CustomTypes;
// Example: 12345.6789 -> { units = 12345, nanos = 678900000 }
message DecimalValue {
// Whole units part of the amount
int64 units = 1;
// Nano units of the amount (10^-9)
// Must be same sign as units
sfixed32 nanos = 2;
nanos
字段表示从 0.999_999_999
到 -0.999_999_999
的值。 例如,decimal
值 1.5m
将表示为 { units = 1, nanos = 500_000_000 }
。 这就是此示例中的 nanos
字段使用 sfixed32
类型的原因:对于较大的值,其编码效率比 int32
更高。 如果 units
字段为负,则 nanos
字段也应为负。
还有多个其他算法可将 decimal
值编码为字节字符串,但此消息比其他任何字符串都易于理解。 在不同的平台上,这些值不会受到字节序的影响。
此类型与 BCL decimal
类型之间的转换可能会在 C# 中实现,如下所示:
namespace CustomTypes;
public partial class DecimalValue
private const decimal NanoFactor = 1_000_000_000;
public DecimalValue(long units, int nanos)
Units = units;
Nanos = nanos;
public static implicit operator decimal(CustomTypes.DecimalValue grpcDecimal)
return grpcDecimal.Units + grpcDecimal.Nanos / NanoFactor;
public static implicit operator CustomTypes.DecimalValue(decimal value)
var units = decimal.ToInt64(value);
var nanos = decimal.ToInt32((value - units) * NanoFactor);
return new CustomTypes.DecimalValue(units, nanos);
每当你使用此类自定义消息类型时,都必须在 .proto
中用注释记录它们。 然后,其他开发人员可以用他们自己的语言或框架实现与等效类型的转换。
上一页下一页