2016-07-27 41 views
-1

首先让我们展示一个简单的测试案例以及如何触发它。这里是这个类:Protobuf.NET - 触发引用列表中的“检测到可能的递归”问题

class ProtoRecurseTest 
    { 
     private int nextPayload = 1; 
     public int Payload { get; private set; } = 0; 
     public ProtoRecurseTest Back { get; private set; } = null; 
     public List<ProtoRecurseTest> Children { get; set; } = new List<ProtoRecurseTest>(); 

     public ProtoRecurseTest Add() 
     { 
      ProtoRecurseTest result = new ProtoRecurseTest(this, nextPayload++); 
      Children.Add(result); 
      return result; 
     } 

     public ProtoRecurseTest() 
     { 
     } 

     private ProtoRecurseTest(ProtoRecurseTest parent, int payload) 
     { 
      Back = parent; 
      this.Payload = payload; 
      nextPayload = payload + 1; 
     } 

     private static void ToStringHelper(ProtoRecurseTest proto, StringBuilder sb) 
     { 
      sb.Append(proto.Payload + " -> "); 

      // another little hassle of protobuf due to empty list -> null deserialization 
      if (proto.Children != null) 
      { 
       foreach (var child in proto.Children) 
        ToStringHelper(child, sb); 
      } 
     } 

     public override string ToString() 
     { 
      StringBuilder sb = new StringBuilder(); 
      ToStringHelper(this, sb); 
      return sb.ToString(); 
     } 
    } 

没有protobuf注释,因为这是由编程的照顾。我已经手动确保将类和Back以及Children都添加到模式中.AsReferenceDefault = true。

递归触发发生在一个实例填充到至少8个足够奇怪的深度时。 7很好。人口代码很简单:

 ProtoRecurseTest recurseTest = new ProtoRecurseTest(); 
     ProtoRecurseTest recurseItem = recurseTest; 
     for (int i = 0; i < 8; i++) 
      recurseItem = recurseItem.Add(); 

然后序列化recurseTest。这种行为只发生在儿童在列表中,但在列表中出现时,即使每个列表中只有1个孩子,也会出现这种情况,因为您最终将从样本填充代码中得出结论。当我用一个参考替换孩子时,所有的事情都很顺利。

这是使用ProtoBuf.NET 2.1.0.0。

回答

0

这是一个解决方案,但我并不特别喜欢。它基于@ marc-gravell对this问题的回答。

class ProtoRecurseTest : ISerializationManagementCallbacks 
    { 
     private int nextPayload = 1; 
     public int Payload { get; private set; } = 0; 
     public ProtoRecurseTest Back { get; private set; } = null; 
     public List<ProtoRecurseTest> Children { get; set; } = new List<ProtoRecurseTest>(); 

     public ProtoRecurseTest Add() 
     { 
      ProtoRecurseTest result = new ProtoRecurseTest(this, nextPayload++); 
      Children.Add(result); 
      return result; 
     } 

     public ProtoRecurseTest() 
     { 
     } 

     private ProtoRecurseTest(ProtoRecurseTest parent, int payload) 
     { 
      Back = parent; 
      this.Payload = payload; 
      nextPayload = payload + 1; 
     } 

     private static void ToStringHelper(ProtoRecurseTest proto, StringBuilder sb) 
     { 
      sb.Append(proto.Payload + " -> "); 

      // another little hassle of protobuf due to empty list -> null deserialization 
      if (proto.Children != null) 
      { 
       foreach (var child in proto.Children) 
        ToStringHelper(child, sb); 
      } 
     } 

     public override string ToString() 
     { 
      StringBuilder sb = new StringBuilder(); 
      ToStringHelper(this, sb); 
      return sb.ToString(); 
     } 

     static void PreSerializationHelper(ProtoRecurseTest instance) 
     { 
      instance.Back = null; 
      foreach (var child in instance.Children) 
       PreSerializationHelper(child); 
     } 

     public void BeforeSerialization() 
     { 
      PreSerializationHelper(this); 
     } 

     static void PostDeserializationHelper(ProtoRecurseTest instance, ProtoRecurseTest parent) 
     { 
      if (instance.Children == null) 
       instance.Children = new List<ProtoRecurseTest>(); 
      instance.Back = parent; 
      foreach (var child in instance.Children) 
       PostDeserializationHelper(child, instance); 
     } 

     public void AfterDeserialization() 
     { 
      PostDeserializationHelper(this, null); 
     } 
    } 

的调用序列化/反序列化现在只需检查类型可以浇铸成ISerializationManagementCallbacks(这只是提供了一个BeforeSerialization和AfterDeserialization法)做他们的生意,然后调用相应的方法如果是这样了。它工作正常。

但是,我并不喜欢将更多的序列化问题混合到我的代码库中 - 这就是为什么以编程方式生成模式的原因。理想情况下,我想完全分开序列化,但空列表 - > null问题(并不是说它是一个“问题”本身,而仅仅是protobuf标准的一个不合时宜的部分)已经使这不可能,但这将是另一个甚至更深奥的问题,我认为这可能确实是一个问题。