Ошибка сериализации Protobuf-net = "Тип не может быть изменен после создания сериализатора" - PullRequest
4 голосов
/ 07 сентября 2011

Следующий сценарий вызывает исключение в Protobuf.net при десериализации. Я сделал что-то не так? Есть ли способ обойти это?

[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 Ответ

3 голосов
/ 07 сентября 2011

В конечном счете, я подозреваю, что проблема здесь:

    [ProtoContract]
    [ProtoInclude(1, typeof(AntRule1))]
    [ProtoInclude(2, typeof(AntRule2))]
    [ProtoInclude(3, typeof(CatRule1))]
    [ProtoInclude(4, typeof(CatRule2))]
    public interface IRule<T> where T : IBeast

Это говорит о том, что для любого T, IRule<T> имеет 4 детей. Это побочный эффект: , если , у вас более одного T, у каждого из AndRule1 ... CatRule2 у каждого "n" родителей, что нехорошо. Давайте вместо этого предположим, что IRule<Ant> имеет 2 правила муравья и так далее ... (в конце концов, я сомневаюсь, что CatRule1 действительно является реализацией IRule<Ant>). В настоящее время это может быть выражено только через RuntimeTypeModel, поскольку атрибуты всегда применяются для all T:

[ProtoContract]
public interface IRule<T> where T : IBeast

и

// 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));

и тогда это работает. Обратите внимание, что настройку необходимо выполнить только один раз, обычно при запуске приложения.


Подумав об этом, я мог бы , вероятно, просто протестировать во время выполнения, а в случае обобщений просто игнорировать все, что не применимо - под этим я подразумеваю при оценке IRule<Dog>, учитывать только типы, если они реализуют IRule<Dog>. Я все еще в раздумье.

...