Разрешено ли <T>в модели ProtoBuf-net во время выполнения? - PullRequest
6 голосов
/ 27 июля 2011

Я использую версию 2 ProtoBuf-net, и в настоящее время я получаю сообщение об ошибке «Невозможно определить элемент: A»

Можно ли создать модель времени выполнения для Protobuf-net, когда мы используем ClassOfType ? Если да, может ли кто-нибудь определить, что мне не хватает в приведенном ниже коде?

btw: этот запрос смоделирован из Десериализовать неизвестный тип с помощью protobuf-net Я мог бы получить версию этого очень хорошо ... но они используют абстрактный базовый класс, а не универсальный класс Т.

ЭТО РАБОЧИЙ ПРИМЕР (вещи, которые не работали, удаляются).

using System;
using System.IO;
using NUnit.Framework;
using ProtoBuf;
using ProtoBuf.Meta;

namespace ProtoBufTestA2
{
    [TestFixture]
    public class Tester
    {
        [Test]
        public void TestMsgBaseCreateModel()
        {
            var BM_SD = new Container<SomeDerived>();

            using (var o = BM_SD) {
                o.prop1 = 42;
                o.payload = new SomeDerived();
                using (var d = o.payload) {
                    d.SomeBaseProp = -42;
                    d.SomeDerivedProp = 62;
                }
            }

            var BM_SB = new Container<SomeBase>();
            using (var o = BM_SB) {
                o.prop1 = 42;
                o.payload = new SomeBase();
                using (var d = o.payload) {
                    d.SomeBaseProp = 84;
                }
            }
            var model = TypeModel.Create();

            model.Add(typeof(Container<SomeDerived>), true);  // BM_SD
            model.Add(typeof(Container<SomeBase>), true);  // BM_SB
            model.Add(typeof(SomeBase), true); // SB
            model.Add(typeof(SomeDerived), true);  // SD
            model[typeof(SomeBase)].AddSubType(50, typeof(SomeDerived)); // SD

            var ms = new MemoryStream();

            model.SerializeWithLengthPrefix(ms, BM_SD, BM_SD.GetType(), ProtoBuf.PrefixStyle.Base128, 0);

            model.SerializeWithLengthPrefix(ms, BM_SB, BM_SB.GetType(), ProtoBuf.PrefixStyle.Base128, 0);
            ms.Position = 0;
            var o1 = (Container<SomeDerived>)model.DeserializeWithLengthPrefix(
                ms
                , null
                , typeof(Container<SomeDerived>), PrefixStyle.Base128, 0);
            var o2 = (Container<SomeBase>)model.DeserializeWithLengthPrefix(
                ms
                , null
                , typeof(Container<SomeBase>), PrefixStyle.Base128, 0);
        }
    }

    [ProtoContract]
    public class Container<T> : IDisposable
    {
        [ProtoMember(1)]
        public int prop1 { get; set; }

        [ProtoMember(2)]
        public T payload { get; set; }

        public void Dispose() { }
    }

    [ProtoContract]
    public class AnotherDerived : SomeDerived, IDisposable
    {
        [ProtoMember(1)]
        public int AnotherDerivedProp { get; set; }
        public override void Dispose() { }
    }

    [ProtoContract]
    public class SomeDerived : SomeBase, IDisposable
    {
        [ProtoMember(1)]
        public int SomeDerivedProp { get; set; }

        public override void Dispose() { }
    }

    [ProtoContract]
    public class SomeBase : IDisposable
    {
        [ProtoMember(1)]
        public int SomeBaseProp { get; set; }

        public virtual void Dispose() { }
    }

    [ProtoContract]
    public class NotInvolved : IDisposable
    {
        [ProtoMember(1)]
        public int NotInvolvedProp { get; set; }
        public void Dispose() { }
    }

    [ProtoContract]
    public class AlsoNotInvolved : IDisposable
    {
        [ProtoMember(1)]
        public int AlsoNotInvolvedProp { get; set; }
        public void Dispose() { }
    }
}

Запрос

Это незначительно, но было бы неплохо, если бы

  (Container<SomeDerived>)model.DeserializeWithLengthPrefix(...) 

также может быть реализован так

  model.DeserializeWithLengthPrefix<Container<SomeDerived>>(...):

Кстати: я начинаю копаться в реализации protobuf-net и начинаю замечать некоторые интересные методы, подобные этому. Что-то, к чему можно вернуться позже, я думаю:

  public MetaType Add(int fieldNumber, string memberName, Type itemType, Type defaultType);

Обсуждение:

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

Вы можете думать об этом в терминах Tupple . Или вариация, например, Tupple >. Это ничем не отличается от List . У меня есть некоторые TreeTypeThings , но мне не нужно их сериализовать / десериализовать (пока).

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

Мне нравится более чистый способ работы с этими идеями. Хотя я могу добраться до того же пункта назначения вручную, Generics делает возможными другие вещи.

re: уточнение

вызывающий может определить все заранее. (кстати: вы заставили меня задуматься о сценарии только во время выполнения, но нет, мне это не нужно).

1 Ответ

4 голосов
/ 28 июля 2011

Так что я думаю вопрос сводится к «могу ли я использовать открытый универсальный тип в качестве шаблона для модели protobuf», и в этом случае ответ «возможно». На данный момент он будет видеть BasicMsg<Foo> и BasicMsg<Bar> как разные типы и по умолчанию будет использовать модель типа атрибута, так как не будет распознавать их как определенные [typeof(BasicMsg<>)]. Если они имеют атрибутов, это, вероятно, сработает, но я не думаю, что это было вашим намерением, верно?

Это интересный сценарий, и я открыт для обсуждения по нему. Тем не менее, одна особая проблема, с которой я здесь столкнулся бы, заключается в том, что природа обобщений в .NET означает, что для этого потребуется участие во время выполнения, то есть RuntimeTypeModel. Я не думаю, что смогу заставить его работать на предварительно скомпилированном TypeModel без использования MakeGenericMethod, которого я действительно хочу избежать по причинам, связанным с платформой и производительностью. Но как полнофункциональная функция .NET только для времени выполнения, она выглядит интересно.

(пояснение к вышесказанному; если вызывающий может заранее определить все T для BasicMsg<T>, это становится немного более выполнимым; тогда это действительно сводится к метафоре шаблона модели)

...