问题
The following scenario seems to cause the exception in Protobuf.net on deserialization. Have I done something wrong? Is there a way round this?
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
public interface IBeast
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
public class Ant : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
public class Cat : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
[ProtoInclude(1, typeof(AntRule1))]
[ProtoInclude(2, typeof(AntRule2))]
[ProtoInclude(3, typeof(CatRule1))]
[ProtoInclude(4, typeof(CatRule2))]
public interface IRule<T> where T : IBeast
{
bool IsHappy(T beast);
}
[ProtoContract]
public class AntRule1 : IRule<Ant>
{
public bool IsHappy(IAnt beast)
{
return true;
}
}
[ProtoContract]
public class AntRule2 : IRule<Ant>
{
public bool IsHappy(IAnt beast)
{
return true;
}
}
[ProtoContract]
public class CatRule1 : IRule<Cat>
{
public bool IsHappy(ICat beast)
{
return true;
}
}
[ProtoContract]
public class CatRule2 : IRule<Cat>
{
public bool IsHappy(ICat beast)
{
return true;
}
}
public class TestSerialization
{
public void Serialize()
{
var antRules = new List<IRule<Ant>>();
antRules.Add(new AntRule1());
antRules.Add(new AntRule2());
var catRules = new List<IRule<Cat>>();
catRules.Add(new CatRule1());
catRules.Add(new CatRule2());
using (var fs = File.Create(@"c:\temp\antRules.bin"))
{
ProtoBuf.Serializer.Serialize(fs, antRules);
fs.Close();
}
using (var fs = File.OpenRead(@"c:\temp\antRules.bin"))
{
List<IRule<Ant>> list;
list = ProtoBuf.Serializer.Deserialize<List<IRule<Ant>>>(fs);
fs.Close();
}
using (var fs = File.Create(@"c:\temp\catRules.bin"))
{
ProtoBuf.Serializer.Serialize(fs, catRules);
fs.Close();
}
using (var fs = File.OpenRead(@"c:\temp\catRules.bin"))
{
List<IRule<Cat>> list;
list = ProtoBuf.Serializer.Deserialize<List<IRule<Cat>>>(fs);
fs.Close();
}
}
}
回答1:
Ultimately I suspect the issue here is:
[ProtoContract]
[ProtoInclude(1, typeof(AntRule1))]
[ProtoInclude(2, typeof(AntRule2))]
[ProtoInclude(3, typeof(CatRule1))]
[ProtoInclude(4, typeof(CatRule2))]
public interface IRule<T> where T : IBeast
This says that for any T
, IRule<T>
has 4 children. This has the side effect of saying if you have more than one T
, each of AndRule1
...CatRule2
each have "n" parents, which isn't good. Let's instead assume that IRule<Ant>
has 2 ant-rules, and so on... (after all, I doubt that CatRule1
is really an implementation of IRule<Ant>
). Currently this can only be expressed via RuntimeTypeModel
, since the attributes would always apply for all T
:
[ProtoContract]
public interface IRule<T> where T : IBeast
and
// note these are unrelated networks, so we can use the same field-numbers
RuntimeTypeModel.Default[typeof(IRule<Ant>)]
.AddSubType(1, typeof(AntRule1)).AddSubType(2, typeof(AntRule2));
RuntimeTypeModel.Default[typeof(IRule<Cat>)]
.AddSubType(1, typeof(CatRule1)).AddSubType(2, typeof(CatRule2));
and then it works. Note the configuration only needs to be done once, usually at app-startup.
Thinking about it, I could probably just test at runtime, and in the case of generics simply ignore any that don't apply - by which I mean when evaluating IRule<Dog>
, only consider the specific types if they implement IRule<Dog>
. I'm still in two minds, though.
来源:https://stackoverflow.com/questions/7333233/protobuf-net-serialization-error-the-type-cannot-be-changed-once-a-serializer