protobuf-net 是否支持可为 null 的类型?

发布于 2024-10-13 11:49:57 字数 170 浏览 4 评论 0原文

是否可以在 protobuf-net 中生成可为 null 的成员?

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

Is it possible to generate nullable members in protobuf-net?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

糖果控 2024-10-20 11:49:57

是的,但如果您从 .proto 进行代码生成,默认情况下它不会生成它们。

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

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

    [ProtoMember(2)]
    public float? Bar {get;set;}
}

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

Yes, but it doesn't generate them by default if you are doing codegen from .proto.

If this is just C#, of course, you don't need a .proto - just:

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

    [ProtoMember(2)]
    public float? Bar {get;set;}
}

If you are working from .proto, you could consider copying and editing csharp.xslt to suit your preferred layout.

不疑不惑不回忆 2024-10-20 11:49:57

是我在使用 Google 的 Protobuf .NET API 时针对可空类型的解决方案,该 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 非长度分隔标量值 - double浮动int32int64uint32uint64sint32sint64fixed32fixed64sfixed32sfixed64布尔


这就是这一切的运作方式。假设您有一条 Record 消息,其中包含一个 NullableInt32 字段,在这个人为的示例中,它是唯一的字段。

Record.proto 中:

syntax = "proto3";

import "NullableInt32.proto";

message Record {
  NullableInt32 id = 1;
}

使用 Google 的 protoc.exe 将其编译为 C# 后,您就可以像对待 Id 属性一样对待 Id 属性。 >可空

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<int>. The signature of the following method is
// bool Equals(int?, int?).
Nullable.Equals<int>(r.Id, 1); // => true

// We can explicitly set Id to a NullableInt32.
r.Id = new NullableInt32 { Value = 1 };

// Just like Nullable<int>, 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

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

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
                 //    }

Here's my solution for nullable types when using Google's Protobuf .NET API which requires Protocol Buffers version 3. (Note that this is not using Marc Gravell's protobuf-net, so this isn't an exact answer to the question asked.)

In NullableInt32.proto:

syntax = "proto3";

message NullableInt32 {
  int32 value = 1;
}

In 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 };
  }
}

This pattern can be used for any of the Protobuf non-length delimited scalar values—double, float, int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, and bool.


Here's how all of this works. Say you have a Record message that has a NullableInt32 field, and in this contrived example it's the only field.

In Record.proto:

syntax = "proto3";

import "NullableInt32.proto";

message Record {
  NullableInt32 id = 1;
}

Once this is compiled to C# with Google's protoc.exe, you can treat the Id property almost exactly like a Nullable<int>.

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<int>. The signature of the following method is
// bool Equals(int?, int?).
Nullable.Equals<int>(r.Id, 1); // => true

// We can explicitly set Id to a NullableInt32.
r.Id = new NullableInt32 { Value = 1 };

// Just like Nullable<int>, 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

Finally, let's look at how our Record message will be transfered over the wire with different values for 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
                 //    }
街角卖回忆 2024-10-20 11:49:57

Proto 3

导入“wrappers.proto”支持可为 null 的值:

  • string(StringValue)、
  • int(Int32Value)、
  • bool(BoolValue)

支持类型的完整列表 - https://learn.microsoft.com/en-us/dotnet/architecture/grpc -for-wcf-developers/protobuf-data-types#nullable-types

示例:

syntax = "proto3";
import "google/protobuf/wrappers.proto";

message ProtoPerson {
    google.protobuf.StringValue firstName = 1;
    google.protobuf.StringValue lastName = 2;
    google.protobuf.StringValue address1 = 3;
    google.protobuf.Int32Value age = 4;
}

Proto 3

Import "wrappers.proto" supports nullable values:

  • string(StringValue),
  • int(Int32Value),
  • bool(BoolValue)
  • and etc

Full list of supported types - https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/protobuf-data-types#nullable-types

Example:

syntax = "proto3";
import "google/protobuf/wrappers.proto";

message ProtoPerson {
    google.protobuf.StringValue firstName = 1;
    google.protobuf.StringValue lastName = 2;
    google.protobuf.StringValue address1 = 3;
    google.protobuf.Int32Value age = 4;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文