protobuf-net是否支持可空类型?

是否有可能在protobuf-net中生成可空的成员?

message ProtoBuf1 { optional Int32? databit = 1; optional Nullable databool = 2; } 

是的,但是如果您从.proto执行codegen,则默认情况下不会生成它们。

如果这只是C#,当然,你不需要 .proto – 只需:

 [ProtoContract] public class ProgoBuf1 { [ProtoMember(1)] public int? Foo {get;set;} [ProtoMember(2)] public float? Bar {get;set;} } 

如果您使用的 .proto,则可以考虑复制和编辑csharp.xslt以适合您的首选布局。

这是我使用Google Protobuf .NET API时可空类型的解决方案,它需要Protocol Buffers版本3.(请注意,这不是使用Marc Gravell的protobuf-net,所以这不是问题的确切答案。)

NullableInt32.proto

 syntax = "proto3"; message NullableInt32 { int32 value = 1; } 

NullableInt32Extensions.cs

 public static class NullableInt32Extensions { public static bool HasValue(this NullableInt32 source) { return source != null; } } public partial class NullableInt32 { public static implicit operator int? (NullableInt32 other) { return other == null ? (int?)null : other.Value; } public static implicit operator NullableInt32(int? other) { return other == null ? null : new NullableInt32 { Value = other.Value }; } } 

此模式可用于任何Protobuf非长度分隔的标量值 – doublefloatint32int64uint32uint64sint32sint64fixed32fixed64sfixed32sfixed64bool


以下是所有这些的工作原理。 假设您有一条具有NullableInt32字段的Record消息,并且在这个设计示例中它是唯一的字段。

Record.proto

 syntax = "proto3"; import "NullableInt32.proto"; message Record { NullableInt32 id = 1; } 

一旦使用Google的protoc.exe将其编译为C#,您就可以将Id属性看作几乎与Nullable完全相同。

 var r = new Record(); // r.Id is null by default, but we can still call HasValue() // because extension methods work on null references. r.Id.HasValue(); // => false // We can explicitly set Id to null. r.Id = null; // We can set Id to a primitive numeric value directly // thanks to our implicit conversion operators. r.Id = 1; // We can also use NullableInt32 in any context that expects a // Nullable. The signature of the following method is // bool Equals(int?, int?). Nullable.Equals(r.Id, 1); // => true // We can explicitly set Id to a NullableInt32. r.Id = new NullableInt32 { Value = 1 }; // Just like Nullable, we can get or set the Value of a // NullableInt32 directly, but only if it's not null. Otherwise, // we'll get a NullReferenceException. Use HasValue() to avoid this. if(r.Id.HasValue()) r.Id.Value.ToString(); // => "1" // Setting Id to 0 is the same as setting Id to a new // NullableInt32 since the default value of int32 is 0. // The following expressions are equivalent. r.Id = 0; r.Id = new NullableInt32(); r.Id = new NullableInt32 { Value = 0 }; r.Id.Value = 0; // as long as Id is not null 

最后,让我们看看我们的Record消息将如何通过线路传输,具有不同的Id值。

 var r = new Record(); // When Id is null, Record is empty since it has no other fields. // Explicitly setting Id to null will have the same effect as // never setting it at all. r.Id = null; r.ToByteArray(); // => byte[0] // Since NullableInt32 is a Protobuf message, it's encoded as a // length delimited type. Setting Id to 1 will yield four bytes. // The first two indicate the type and length of the NullableInt32 // message, and the last two indicate the type and value held within. r.Id = 1; r.ToByteArray(); // => byte[] { // 0x0a, // field = 1, type = 2 (length delimited) // 0x02, // length = 2 // 0x08, // field = 1, type = 0 (varint) // 0x01, // value = 1 // } // When Id is set to the default int32 value of 0, only two bytes // are needed since default values are not sent over the wire. // These two bytes just indicate that an empty NullableInt32 exists. r.Id = 0; r.ToByteArray(); // => byte[] { // 0x0a, // field = 1, type = 2 (length delimited) // 0x00, // length = 0 // }