<T> 在运行时 ProtoBuf-net 模型中是不是允许?

Posted

技术标签:

【中文标题】<T> 在运行时 ProtoBuf-net 模型中是不是允许?【英文标题】:Is of <T> allowed in a run-time ProtoBuf-net model?<T> 在运行时 ProtoBuf-net 模型中是否允许? 【发布时间】:2011-07-27 17:23:32 【问题描述】:

我使用的是 ProtoBuf-net 版本 2,目前我收到错误“无法确定成员:A”

当我们使用 ClassOfType 时,是否可以为 Protobuf-net 创建运行时模型?如果是这样,谁能发现我在下面的代码中缺少什么?

顺便说一句:这个请求是根据Deserialize unknown type with 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);

讨论:

当我在上面的链接中看到可以反序列化为抽象基类型的方式时,我想,是的,这更接近于我的想法。我们是否可以先反序列化为开放的通用 Container,然后如果需要在不同的程序集中进行更具体的转换。也许我在这里有点混淆了。

你可以从元组的角度来考虑它。或者像 Tuple> 这样的变体。它与 List 没有什么不同。我也有一些 TreeTypeThings,但我不需要序列化/反序列化它们(还)。

我有一个非通用序列工作,所以它不是一个展示停止器。我的第一个实现可能会更有效。不过,我认为使用现有的 protobuf-net 功能可以做得更好。

我喜欢以更简洁的通用方式处理这些想法。虽然我可以手动到达同一个目的地,但泛型使其他事情成为可能。

re: 澄清

调用者可以提前定义一切。 (顺便说一句:你现在让我想到了仅运行时的场景,但不,我不需要那个)。

【问题讨论】:

Re "无法确定成员:A" - 这仅仅是因为没有名为A的成员,顺便说一句。同样B,C,DE @Marc Gravell 我会看看的。我认为 .Add(1, "A") 也创建了标签。它不能走那么远。说得通。哎呀。 @Marc Gravell:我最终一切正常 + 更改了上面的示例代码以反映这一点。再次感谢。让我们把开放的泛型留到其他时间看看。 【参考方案1】:

所以我认为问题归结为“我可以使用开放的泛型类型作为 protobuf 模型的模板吗”,在这种情况下,答案是“也许”。 目前,它会将BasicMsg&lt;Foo&gt;BasicMsg&lt;Bar&gt; 视为不同的类型,并且默认使用属性类型模型,因为它不会识别它们是由[typeof(BasicMsg&lt;&gt;)] 定义的.如果他们属性,它可能会工作,但我不认为这是你的意图,对吧?

这是一个有趣的场景,我愿意对此进行讨论。但是,我在这里特别担心的是.NET 中泛型的性质意味着这需要运行时参与,即RuntimeTypeModel。我认为如果不使用 MakeGenericMethod 就无法在 预编译 TypeModel 上运行它,出于平台和性能原因,我真的想避免这种情况。但作为一个完整的.NET 运行时特性,它看起来很有趣。

(澄清以上内容;如果调用者可以提前为BasicMsg&lt;T&gt; 定义所有T,它变得稍微可行;那么它真的归结为模型模板隐喻)

【讨论】:

感谢您的浏览。很亲切。上面的评论,而不是试图将它们放入推文 x 3。 可以用原始类型序列化对象吗?我在模型中尝试了很多组合。顺便说一句:您愿意将此请求作为一个新问题吗? @sgtz 你能说得更具体点吗? 在***.com/questions/6891415/…发布的新问题 @sgtz 如果它是这个主题的主题,只需编辑它并让我知道

以上是关于<T> 在运行时 ProtoBuf-net 模型中是不是允许?的主要内容,如果未能解决你的问题,请参考以下文章

在运行时指定通用集合类型参数

我们可以在运行时创建 List<user defined datatype> 吗?

HttpClient.ReadAsAsync<T> 使用 [Serializable] 时返回空对象

Java中定义泛型<T>时,怎么获得泛型的类型

如何在运行时获取泛型的类型

1026 程序运行时间