ProtoBuf-net AsReference 需要 Activator.CreateInstance 中的公共构造函数?

Posted

技术标签:

【中文标题】ProtoBuf-net AsReference 需要 Activator.CreateInstance 中的公共构造函数?【英文标题】:ProtoBuf-net AsReference require public constructor in Activator.CreateInstance? 【发布时间】:2011-08-28 09:01:54 【问题描述】:

在我的 2 个看起来像这样(最小)的类上工作时

using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using ProtoBuf;

namespace Sandbox

    public partial class Form1 : Form
    
        public Form1()
        
            Family family = new Family();
            Child child1 = new Child(1);
            Child child2 = new Child(2);
            Parent parent = new Parent(new List<Child>()  child1, child2 );
            family.Add(parent);

            string file = "sandbox.txt";

            try  File.Delete(file);  catch  

            using (var fs = File.OpenWrite(file))  Serializer.Serialize(fs, family); 
            using (var fs = File.OpenRead(file))  family = Serializer.Deserialize<Family>(fs); 

            System.Diagnostics.Debug.Assert(family != null, "1. Expect family not null, but not the case.");
        
    

    [ProtoContract()]
    public class Child
    
        [ProtoMember(1, AsReference = true)]
        internal Parent Parent;

        private Child()  

        public Child(int i)  
    

    [ProtoContract()]
    public class Parent
    
        [ProtoMember(1)]
        protected List<Child> m_Children;

        /// <summary>
        /// ProtoBuf deserialization constructor (fails here)
        /// </summary>
        private Parent()  m_Children = new List<Child>(); 

        public Parent(List<Child> children)
        
            m_Children = children;
            m_Children.ForEach(x => x.Parent = this);
        
    

    [ProtoContract()]
    public class Family
    
        [ProtoMember(1)]
        protected List<Parent> m_Parents;

        public void Add(Parent parent)
        
            m_Parents.Add(parent);
        

        public Family()
        
            m_Parents = new List<Parent>();
        
    

在反序列化期间,我遇到异常“没有为此对象定义无参数构造函数”。用于在 ProtoBuf.BclHelper 附近创建 Parent 对象

case FieldObject:
// ...
value = ((options & NetObjectOptions.UseConstructor) == 0) ? BclHelpers.GetUninitializedObject(type) : Activator.CreateInstance(type);

然后当我将默认构造函数 Parent() 更改为 public 时,异常消失了。

知道在这种情况下,AsRerference 的正确用法是什么我可能忽略了

赏金: 虽然 Marc 花时间解决这个问题,但我需要一个明确的解决方案来在这种情况下使用 protobuf-net,通过 protobuf-net 属性、方法或其他技巧来解决。否则我将不得不完全放弃使用 protobuf-net。感谢您的帮助。

【问题讨论】:

我刚从几天外回来。我稍后会看这个。 简单地看一下,我认为有一个与继承和引用跟踪有关的极端情况;我知道如何解决它 - 它只需要一些调整即可修复。 @Marc 感谢您的光临。真的很感谢你的贡献。如果您有时间对此进行排序,希望您会在此处更新答案。再次感谢。 我希望在一两天内修复它 【参考方案1】:

我相信你可以这样做来解决这个问题:

[ProtoContract(SkipConstructor = true)]
public class Parent

    [ProtoMember(1)]
    protected List<Child> m_Children;

    private Parent()  Initialize(); 

    [ProtoBeforeDeserialization] // could also use OnDeserializing
    private void Initialize()
    
        m_Children = new List<Child>();
    

    public Parent(List<Child> children)
    
        m_Children = children;
        m_Children.ForEach(x => x.Parent = this);
    

【讨论】:

谢谢西蒙。但我不想跳过构造函数,因为那里正在进行一些初始化。 谢谢。这很有帮助,尽管结果会很混乱,绝对不会是永久性的解决方案。顺便说一句,你碰巧知道 protobuf-net 文档的地方吗?如果我可以谷歌搜索“protobuf-net 属性列表”,那就太好了 @Jake - 是的,这只是一个快速修复,显然库需要一些修改。而且,不,我不知道protobuf-net文档有什么秘密的地方,但是.NET Reflector是我的上帝,OnDeserializing的技巧原理和使用WCF时完全一样,我碰巧知道一点。跨度> 【参考方案2】:

反序列化Parent 时,需要公共无参数构造函数。因此,失败的最小测试用例是:创建具有非空 m_ParentChild,对其进行序列化并反序列化结果。

【讨论】:

对不起,我不太了解你。但我应该更清楚。我正在反序列化父级。我已经进入了 PB-net 源代码,看到它在尝试创建 Parent 时抛出。 好吧,现在我也不明白发生了什么。 另外,我的最小测试用例使用私有或受保护的无参数构造函数通过。我只需要知道在使用 AsReference 时还有哪些其他事情会间接导致私有构造函数失败。

以上是关于ProtoBuf-net AsReference 需要 Activator.CreateInstance 中的公共构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

ProtoBuf-net AsReference 需要 Activator.CreateInstance 中的公共构造函数?

Protobuf-net 对象图引用完整性

检测到 Protobuf-net 可能的递归:序列化孩子和父母

反序列化带有标记为 AsReference 的成员的类时删除了 ProtoException

如何在 Protobuf-net 中动态添加 Proto 成员

protobuf-net:日期时间的编码