• 当你处理有符号的值时, 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# 类型 已知类型包装器

    已知类型 TimestampDuration 以 .NET 形式表示为类。 在 C# 8 和更高版本中,可以使用可以为 null 的引用类型。 但在转换为 DateTimeOffsetTimeSpan 时,请务必检查这些类型的属性是否为 null。

    Protobuf 本身不支持 .NET decimal 类型,只支持 doublefloat。 在 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 的值。 例如,decimal1.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 中用注释记录它们。 然后,其他开发人员可以用他们自己的语言或框架实现与等效类型的转换。

    上一页下一页