如何正确使用派生 类 作为 Microsoft Bond 对象的字段
How do I correctly use derived classes as fields of a Microsoft Bond object
所以没有混淆,当我讨论我的问题时,我是作为使用编译的 classes 的人这样做的,这些结果来自 Bond 模式(也就是说我使用 "class"而不是 "struct",等等)。我觉得这样想更有意义。
我正在使用 Microsoft Bond,我有一个主 class,它有几个属性,其中之一是派生 class 的一个实例。
创建主 class 的实例时,我可以将 属性 设置为派生 class 的实例;然而,当我从二进制反序列化回主 class 时,属性 现在被视为它的基础 class.
我试图将其转换为派生的 class,但这会引发运行时异常。
在 Bond documentation/manual 中使用派生 classes 的示例是否在反序列化时指定了派生 class,但我不只是反序列化派生 class 但主要 class.
这是我如何设置绑定模式的示例
struct BaseExample
{
0: int property1;
}
struct DerivedExample : BaseExample
{
0: int property2;
}
struct MainExample
{
0: BaseExample mainProperty;
}
在使用中,我将 mainProperty 设置为 DerivedExample class 的一个实例。
我期望的是,在反序列化之后,mainProperty 仍然是 DerivedExample 类型(包含 属性2),但我看到的是 mainProperty 是 BaseExample 类型(并且不包含 属性2)
我是被迫使用泛型来做这件事还是我遗漏了什么?
编辑:添加示例
我使用从 Bond 模式生成的 classes 的代码是这样的。
我们有一个调用服务可以创建这种类型的消息,并使用 Bond 将其序列化为字节数组,然后再通过流发送。
var message = new MainExample();
var derivedExample = new DerivedExample()
{
property1 = 1,
property2 = 2,
};
message.mainProperty = derivedExample;
// This block is all from the Bond examples
var output = new OutputBuffer();
var writer = new CompactBinaryWriter<OutputBuffer>(output);
Serialize.To(writer, message);
SendMessage(output.Data.Array);
现在我们有一个接收服务,它将把这个消息从流中取出并使用 Bond 将它反序列化回一个对象。
void HandleMessage(byte[] messageBA)
{
// This block is all from the Bond examples
var input = new InputBuffer(messageBA);
var reader = new CompactBinaryReader<InputBuffer>(input);
MainExample message = Deserialize<BondEvent>.From(reader);
// mainProperty is now of type BaseExample and not DerivedExample
message.mainProperty.property1; // is accessable
message.mainProperty.property2; // will not compile
DerivedExample castedProperty = message.mainProperty as DerivedExample; // fails at runtime
}
完全披露:我实际上使用的是 F#,但我认为在 C# 中执行这些操作会更好
您在反序列化时观察到的切片行为与编写的模式一致。 MainExample.mainProperty
字段是BaseExample
类型的,所以在序列化时,只写入BaseExample
字段。使用哪种运行时类型并不重要。另外,反序列化时,只有BaseExample
个字段会被实现。
在处理继承和多态性时,Bond 不在序列化有效载荷中包含任何类型信息:它将如何建模的决定留给架构设计者。这源于邦德的只为使用付费的理念。
根据您建模的数据,我看到了两种设计架构的方法:
- 仿制药
bonded
泛型
如问题中所述,MainExample
结构可以通用:
struct MainExample<T>
{
0: T mainProperty;
}
这实质上允许您轻松创建一堆具有相似形状的不同结构。但是这些结构不会有 "is a" 关系。 HandleMessage
之类的方法可能也必须是通用的,从而导致通用级联。
保税
要在另一个类型多态的结构中包含一个字段,请将该字段设置为 bonded
field。绑定字段在序列化时不切片。此外,它们不会立即反序列化,因此接收方有机会选择合适的类型进行反序列化。
在 .bond 文件中,我们有这个:
struct MainExample
{
0: bonded<BaseExample> mainProperty;
}
要序列化,如下:
var message = new MainExample();
var derivedExample = new DerivedExample()
{
property1 = 1,
property2 = 2,
};
message.mainProperty = new Bonded<DerivedExample>(derivedExample);
// NB: new Bonded<BaseExample>(derivedExample) WILL slice
反序列化:
void HandleMessage(byte[] messageBA)
{
// This block is all from the Bond examples
var input = new InputBuffer(messageBA);
var reader = new CompactBinaryReader<InputBuffer>(input);
MainExample message = Deserialize<BondEvent>.From(reader);
DerivedExample de = message.mainProperty.Deserialize<DerivedExample>();
}
当使用 bonded
字段进行多态性时,我们需要有一些方法来知道要反序列化到哪个 most-derived 类型。有时这是从有效载荷外部的上下文中获知的(例如,可能处理的每个消息只有一种类型)。其他时候,我们需要将此信息嵌入有效负载的公共部分。一种常见的方法是使用枚举:
enum PropertyKind
{
Base;
Derived;
}
struct MainExample
{
0: bonded<BaseExample> mainProperty;
1: PropertyKind mainPropertyKind = Base;
}
在 Bond 存储库的 C# polymorphic_container 示例中有一个执行这种调度的完整示例。
OutputBuffer.Data.Array
我注意到在发送消息的代码中,有以下一行,其中包含一个错误:
SendMessage(output.Data.Array);
OutputBuffer.Data
属性 是一个 ArraySegment<byte>
,用于表示其他数组的切片。此切片可能比整个数组(Count
属性)短,并且它可能从 0 以外的偏移量开始(Offset
属性)。大多数 I/O 库都有像 SendMessage(byte[] buf, int offset, int count)
这样的重载,可以在这种情况下使用。
支持 OutputBuffer
的默认数组是 65K,因此几乎可以肯定发送了一堆额外的数据。
所以没有混淆,当我讨论我的问题时,我是作为使用编译的 classes 的人这样做的,这些结果来自 Bond 模式(也就是说我使用 "class"而不是 "struct",等等)。我觉得这样想更有意义。
我正在使用 Microsoft Bond,我有一个主 class,它有几个属性,其中之一是派生 class 的一个实例。
创建主 class 的实例时,我可以将 属性 设置为派生 class 的实例;然而,当我从二进制反序列化回主 class 时,属性 现在被视为它的基础 class.
我试图将其转换为派生的 class,但这会引发运行时异常。
在 Bond documentation/manual 中使用派生 classes 的示例是否在反序列化时指定了派生 class,但我不只是反序列化派生 class 但主要 class.
这是我如何设置绑定模式的示例
struct BaseExample
{
0: int property1;
}
struct DerivedExample : BaseExample
{
0: int property2;
}
struct MainExample
{
0: BaseExample mainProperty;
}
在使用中,我将 mainProperty 设置为 DerivedExample class 的一个实例。 我期望的是,在反序列化之后,mainProperty 仍然是 DerivedExample 类型(包含 属性2),但我看到的是 mainProperty 是 BaseExample 类型(并且不包含 属性2)
我是被迫使用泛型来做这件事还是我遗漏了什么?
编辑:添加示例
我使用从 Bond 模式生成的 classes 的代码是这样的。
我们有一个调用服务可以创建这种类型的消息,并使用 Bond 将其序列化为字节数组,然后再通过流发送。
var message = new MainExample();
var derivedExample = new DerivedExample()
{
property1 = 1,
property2 = 2,
};
message.mainProperty = derivedExample;
// This block is all from the Bond examples
var output = new OutputBuffer();
var writer = new CompactBinaryWriter<OutputBuffer>(output);
Serialize.To(writer, message);
SendMessage(output.Data.Array);
现在我们有一个接收服务,它将把这个消息从流中取出并使用 Bond 将它反序列化回一个对象。
void HandleMessage(byte[] messageBA)
{
// This block is all from the Bond examples
var input = new InputBuffer(messageBA);
var reader = new CompactBinaryReader<InputBuffer>(input);
MainExample message = Deserialize<BondEvent>.From(reader);
// mainProperty is now of type BaseExample and not DerivedExample
message.mainProperty.property1; // is accessable
message.mainProperty.property2; // will not compile
DerivedExample castedProperty = message.mainProperty as DerivedExample; // fails at runtime
}
完全披露:我实际上使用的是 F#,但我认为在 C# 中执行这些操作会更好
您在反序列化时观察到的切片行为与编写的模式一致。 MainExample.mainProperty
字段是BaseExample
类型的,所以在序列化时,只写入BaseExample
字段。使用哪种运行时类型并不重要。另外,反序列化时,只有BaseExample
个字段会被实现。
在处理继承和多态性时,Bond 不在序列化有效载荷中包含任何类型信息:它将如何建模的决定留给架构设计者。这源于邦德的只为使用付费的理念。
根据您建模的数据,我看到了两种设计架构的方法:
- 仿制药
bonded
泛型
如问题中所述,MainExample
结构可以通用:
struct MainExample<T>
{
0: T mainProperty;
}
这实质上允许您轻松创建一堆具有相似形状的不同结构。但是这些结构不会有 "is a" 关系。 HandleMessage
之类的方法可能也必须是通用的,从而导致通用级联。
保税
要在另一个类型多态的结构中包含一个字段,请将该字段设置为 bonded
field。绑定字段在序列化时不切片。此外,它们不会立即反序列化,因此接收方有机会选择合适的类型进行反序列化。
在 .bond 文件中,我们有这个:
struct MainExample
{
0: bonded<BaseExample> mainProperty;
}
要序列化,如下:
var message = new MainExample();
var derivedExample = new DerivedExample()
{
property1 = 1,
property2 = 2,
};
message.mainProperty = new Bonded<DerivedExample>(derivedExample);
// NB: new Bonded<BaseExample>(derivedExample) WILL slice
反序列化:
void HandleMessage(byte[] messageBA)
{
// This block is all from the Bond examples
var input = new InputBuffer(messageBA);
var reader = new CompactBinaryReader<InputBuffer>(input);
MainExample message = Deserialize<BondEvent>.From(reader);
DerivedExample de = message.mainProperty.Deserialize<DerivedExample>();
}
当使用 bonded
字段进行多态性时,我们需要有一些方法来知道要反序列化到哪个 most-derived 类型。有时这是从有效载荷外部的上下文中获知的(例如,可能处理的每个消息只有一种类型)。其他时候,我们需要将此信息嵌入有效负载的公共部分。一种常见的方法是使用枚举:
enum PropertyKind
{
Base;
Derived;
}
struct MainExample
{
0: bonded<BaseExample> mainProperty;
1: PropertyKind mainPropertyKind = Base;
}
在 Bond 存储库的 C# polymorphic_container 示例中有一个执行这种调度的完整示例。
OutputBuffer.Data.Array
我注意到在发送消息的代码中,有以下一行,其中包含一个错误:
SendMessage(output.Data.Array);
OutputBuffer.Data
属性 是一个 ArraySegment<byte>
,用于表示其他数组的切片。此切片可能比整个数组(Count
属性)短,并且它可能从 0 以外的偏移量开始(Offset
属性)。大多数 I/O 库都有像 SendMessage(byte[] buf, int offset, int count)
这样的重载,可以在这种情况下使用。
支持 OutputBuffer
的默认数组是 65K,因此几乎可以肯定发送了一堆额外的数据。