ProtoBuf-Net ProtoInclude подкласс универсального типа - PullRequest
4 голосов
/ 23 февраля 2012

У меня возникли некоторые проблемы с ProtoBuf-Net с подклассом объекта, который наследуется от универсального класса.

Мое дерево наследования выглядит так:

Node
    SomeNodeType
    SomeOtherType
    ResourceNode<T>
        ShipResource : ResourceNode<Ship>
        SomeResource : ResourceNode<SomeType>

Я использую ProtoInclude для базового типа узла для всех обычных типов.

Каков наилучший способ достижения этой иерархии с помощью protobuf-net? Я пытался просто включить все, но я получаю ошибки, которые, похоже, происходят из-за protobuf, пытающегося десериализовать объект как один из его родительских объектов.

Ответы [ 2 ]

4 голосов
/ 23 февраля 2012

Вы, вероятно, видите:

Тип может участвовать только в одной иерархии наследования

на данный момент, верно?

Проблемастановится понятнее, если вспомнить, что ResourceNode<T> не является закрытым типом, но ResourceNode<Ship> и ResourceNode<SomeType> равны .Это означает 2 вещи:

Во-первых, Node нужно знать отдельно о двух (ResourceNode<Ship> и ResourceNode<SomeType>), а во-вторых: нам нужно рассказать ResourceNode<Ship> о ShipResource только и ResourceNode<SomeType> о SomeResource только .

Первое достаточно просто с использованием атрибутов:

[ProtoContract]
[ProtoInclude(1, typeof(SomeNodeType)), ProtoInclude(2, typeof(SomeOtherType))]
[ProtoInclude(3, typeof(ResourceNode<Ship>))]
[ProtoInclude(4, typeof(ResourceNode<SomeType>))]
public class Node { }

Однако второеБит не может быть четко выражен в любом текущем выпуске.В настоящее время мы не можем использовать:

[ProtoContract]
[ProtoInclude(1, typeof(ShipResource)), ProtoInclude(1, typeof(SomeResource))]
public class ResourceNode<T> : Node { }

, поскольку эти атрибуты применяются к как ResourceNode<Ship> и ResourceNode<SomeType> и представляют собой незаконные цепочки наследования.Дублированный 1 в вышеупомянутом является преднамеренным, поскольку они не в конфликте, опять же, потому что они являются параллельными ветвями., настроить эту связь явно:

RuntimeTypeModel.Default.Add(typeof(ResourceNode<Ship>), true)
     .AddSubType(1, typeof (ShipResource));
RuntimeTypeModel.Default.Add(typeof(ResourceNode<SomeType>), true)
     .AddSubType(1, typeof(SomeResource));

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

[ProtoContract]
[ProtoInclude(1, typeof(ShipResource)), ProtoInclude(1, typeof(SomeResource))]
public class ResourceNode<T> : Node { }

Я добавил элемент "todo" и провал тест для этого.Тем не менее, интересно, что при настройке я также нашел сценарий, где что-то не работает, поэтому мне нужно сначала исправить это

1 голос
/ 18 сентября 2013

У меня была точно такая же проблема, но вместо настройки всех типов вручную приведенный ниже метод работает для любого типа.Вызовите его до сериализации / десериализации.

private void PopulateTypes(Type t)
{
    foreach(object mt in RuntimeTypeModel.Default.GetTypes())
    {
        MetaType theType = mt as MetaType;
        if (null != theType)
        {
            if (theType.Type == t)
                return;
        }
    }

    Type objType = typeof (object);
    List<Type> inheritanceTree = new List<Type>();
    do
    {
        inheritanceTree.Insert(0, t);
        t = t.BaseType;
    } while (null != t && t != objType);

    if (!inheritanceTree.Any(gt => gt.IsGenericType))
        return;

    int n = 100;
    for (int i = 0; i < inheritanceTree.Count - 1; i++)
    {
        Type type = inheritanceTree[i];
        MetaType mt = RuntimeTypeModel.Default.Add(type, true);
        mt.AddSubType(n++, inheritanceTree[i + 1]);
    }
}
...