如果我的测试设置为
[ProtoContract]
[ProtoInclude(1, typeof(DecoratorDerived))]
public class DecoratorBase
{
[ProtoMember(2)]
private int baseValue1;
private int baseValue2;
public DecoratorBase()
{
baseValue1 = (new Random()).Next();
baseValue2 = (new Random()).Next();
}
protected void ShowBaseValue()
{
Console.WriteLine($"DecoratorBase - baseValue1: {baseValue1}, baseValue2: {baseValue2}");
}
}
[ProtoContract]
public class DecoratorDerived : DecoratorBase
{
[ProtoMember(2)]
public int derivedValue1;
private int derivedValue2;
public DecoratorDerived()
{
derivedValue1 = (new Random()).Next();
derivedValue2 = (new Random()).Next();
}
public void ShowValues()
{
ShowBaseValue();
Console.WriteLine($"DecoratorDerived - derivedValue1: {derivedValue1}, derivedValue2: {derivedValue2}");
}
}
static void DecoratorTest()
{
var c1 = new DecoratorDerived();
c1.ShowValues();
byte[] raw;
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<DecoratorDerived>(stream, c1);
raw = stream.ToArray();
}
DecoratorDerived c2;
using (var stream = new MemoryStream(raw))
{
c2 = ProtoBuf.Serializer.Deserialize<DecoratorDerived>(stream);
}
c2.ShowValues();
}
一切正常,但实际问题是我的基础类是通过T4自动生成的,并且它们很多,因此添加所有ProtoInclude
行是不可行的。进行的一些研究表明,使用RuntimeTypeModel可以即时执行操作。因此将测试更改为
[ProtoContract]
public class RTMBase
{
[ProtoMember(2)]
private int baseValue1;
private int baseValue2;
public RTMBase()
{
baseValue1 = (new Random()).Next();
baseValue2 = (new Random()).Next();
}
protected void ShowBaseValue()
{
Console.WriteLine($"RTMBase - baseValue1: {baseValue1}, baseValue2: {baseValue2}");
}
}
[ProtoContract]
public class RTMDerived : RTMBase
{
[ProtoMember(2)]
public int derivedValue1;
private int derivedValue2;
public RTMDerived()
{
derivedValue1 = (new Random()).Next();
derivedValue2 = (new Random()).Next();
}
public void ShowValues()
{
ShowBaseValue();
Console.WriteLine($"RTMDerived - derivedValue1: {derivedValue1}, derivedValue2: {derivedValue2}");
}
}
static void RTMTest(RuntimeTypeModel runtimeTypeModel)
{
var c1 = new RTMDerived();
c1.ShowValues();
// setup RTM, https://stackoverflow.com/questions/40608767/inheritance-in-protobuf-net-adding-a-lower-base-class-still-backward-compatible
var myType = runtimeTypeModel[typeof(RTMDerived)];
var baseType = runtimeTypeModel[typeof(RTMDerived).BaseType];
if (!baseType.GetSubtypes().Any(s => s.DerivedType == myType))
{
foreach (var field in baseType.GetFields())
{
myType.Add(field.FieldNumber + 500, field.Name);
}
}
byte[] raw;
using(var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<RTMDerived>(stream, c1);
raw = stream.ToArray();
}
RTMDerived c2;
using(var stream = new MemoryStream(raw))
{
c2 = ProtoBuf.Serializer.Deserialize<RTMDerived>(stream);
}
c2.ShowValues();
}
我有例外
RTMBase - baseValue1: 1874947795, baseValue2: 1391655165
RTMDerived - derivedValue1: 922997568, derivedValue2: 837049520
Unhandled Exception: System.ArgumentException: Unable to determine member: baseValue1
Parameter name: memberName
at ProtoBuf.Meta.MetaType.AddField(Int32 fieldNumber, String memberName, Type itemType, Type defaultType, Object defaultValue) in C:\code\protobuf-net\src\protobuf-net\Meta\MetaType.cs:line 1437
at ProtoBuf.Meta.MetaType.Add(Int32 fieldNumber, String memberName) in C:\code\protobuf-net\src\protobuf-net\Meta\MetaType.cs:line 1261
at proto_error.Program.RTMTest1(RuntimeTypeModel runtimeTypeModel) in /Users/christian/tmp/proto-error/Program.cs:line 52
at proto_error.Program.Main(String[] args) in /Users/christian/tmp/proto-error/Program.cs:line 17
对我来说幸运的是,将RTMBase.baseValue1
更改为public,protected或internal使其可以工作,而internal对于我的用例来说就很好。但是我很好奇这是一个错误还是做错了什么?
添加
如果我将RMTest更改为
static void RTMTest2(RuntimeTypeModel runtimeTypeModel)
{
var c1 = new RTMDerived();
c1.ShowValues();
// setup RTM, https://stackoverflow.com/questions/10400539/protobuf-net-runtimetypemodel-not-serializing-members-of-base-class
var baseType = runtimeTypeModel[typeof(RTMDerived).BaseType];
baseType.AddSubType(500, typeof(RTMDerived));
byte[] raw;
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<RTMDerived>(stream, c1);
raw = stream.ToArray();
}
RTMDerived c2;
using (var stream = new MemoryStream(raw))
{
c2 = ProtoBuf.Serializer.Deserialize<RTMDerived>(stream);
}
c2.ShowValues();
}
它工作得很好,但是我仍然很好奇RTMRest1
为何失败。另外,每种类型的500参数是否需要不同,还是可以为常数?
答案 0 :(得分:0)
提供的密钥用于每次唯一地标识为父邮件中的一个字段,所以是的:每个类型的密钥需要且可靠-例如,如果现在为500,则为在两年内重新加载数据时,也需要为500。
这也适用于字段编号,因此此处显示的代码非常危险:
foreach (var field in baseType.GetFields())
{
myType.Add(field.FieldNumber + 500, field.Name);
}
至于找不到成员,我想它是在寻找它的声明成员。尽管可能会造成歧义和脆弱的情况,但可能会改变它们。以下代码是完全有效的,并且可以正常编译:
class Foo
{
private int a;
}
class Bar : Foo
{
private int a;
}
但是"a"
的含义会根据我们在考虑Foo
还是Bar
而变化。对于您的情况(将基本成员解析为派生类型),您想到的是Foo.a
,但您可能会要求它从Bar
开始,因此要解决的逻辑问题是{ {1}}。我认为(从内存中),这就是为什么我们将事情限制为声明的成员。