c# - 如何在 Protobuf 中实现 VARIANT

标签 c# .net protocol-buffers protobuf-net

作为我的 protobuf 协议(protocol)的一部分,我需要能够发送动态类型的数据,有点像 VARIANT .粗略地说,我要求数据是整数、字符串、 bool 值或“其他”,其中“其他”(例如 DateTime)被序列化为字符串。我需要能够将它们用作单个字段,并在协议(protocol)中多个不同位置的列表中使用。

如何在保持最小消息大小和最佳性能的同时最好地实现这一点?

我在 C# 中使用 protobuf-net。

编辑:
我在下面发布了一个建议的答案,它使用了我认为所需的最小内存。

编辑 2:
http://github.com/pvginkel/ProtoVariant 创建了一个 github.com 项目具有完整的实现。

最佳答案

Jon 的多个选项涵盖了最简单的设置,尤其是在您需要跨平台支持的情况下。在 .NET 方面(确保您不会序列化不必要的值),只需从任何不匹配的属性返回 null,例如:

public object Value { get;set;}
[ProtoMember(1)]
public int? ValueInt32 {
    get { return (Value is int) ? (int)Value : (int?)null; }
    set { Value = value; }
}
[ProtoMember(2)]
public string ValueString {
    get { return (Value is string) ? (string)Value : null; }
    set { Value = value; }
}
// etc

如果您不喜欢空值,您也可以使用 bool ShouldSerialize*() 模式执行相同的操作。

将其包装在 class 中,您应该可以在字段级别或列表级别使用它。您提到最佳性能;我唯一可以建议的是考虑将其视为“组”而不是“子消息”,因为这更容易编码(并且同样容易解码,只要您期望数据)。为此,请通过 Grouped 使用 [ProtoMember] 数据格式,即

[ProtoMember(12, DataFormat = DataFormat.Group)]
public MyVariant Foo {get;set;}

然而,这里的区别可能很小——但它避免了输出流中的一些回溯来固定长度。无论哪种方式,就开销而言,“子消息”至少需要 2 个字节; “至少一个”用于字段 header (如果 12 实际上是 1234567 则可能需要更多) - 并且“至少一个”用于长度,对于较长的消息会变得更大。一个组占用 2 倍的字段 header ,因此如果您使用低字段编号,则无论封装数据的长度如何(它可能是 5MB 的二进制文件),这都将是 2 个字节。

一个单独的技巧,对更复杂的场景有用但不是互操作的,是通用继承,即一个抽象基类,它有 ConcreteType<int>ConcreteType<string> 等列为子类型 - 然而,这需要额外的 2 个字节(通常),所以没那么节俭。

远离核心规范,如果您真的不能告诉您需要支持哪些类型,并且不需要互操作性 - 有一些支持在数据中包含(优化的)类型信息;请参阅 DynamicType 上的 ProtoMember 选项 - 这比其他两个选项占用更多空间。

关于c# - 如何在 Protobuf 中实现 VARIANT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6519533/

相关文章:

c# - 使用 WebDAV 和 C# 读取电子邮件的主题和正文

c# - 如何将字符串转换为 OctetString (C#)?

java - 当您在 Java 中实现接口(interface)时,它是显式的还是隐式的?

c# - 如何在 .NET 中解析联合和列表类型的值?

java - 如何为spring kafka设置protobuf参数?

java - 在 Flink SourceFunction 中获取 ClassNotFound 异常

go - 如何从 FieldDescriptor 和 protoreflect.Range 中的 Value 获取字段值?

c# - 在一台 PC 上使用多个 Kinect v2

c# - 从 silverlight 调用 Java Web 服务会引发异常

c# - (可选)将结构传递给 C# 中的自定义函数