Anders Forsgren
Anders Forsgren

Reputation: 11101

Protobuf-net null/empty lists and reference equality

How can I configure the protobuf-net typemodel to pass the 3 unit tests in the example below? Protobuf version is v2 r470.

I have looked at the list tests in the svn tree briefly but can't spot the difference between this and the null vs empty tests in protobuf-net svn.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using ProtoBuf; 
using ProtoBuf.Meta;

namespace ProtoCollections
{ 
   [TestFixture]
   public class CollectionTests
   {
      [Test]
      public void TestEmptyList()
      {
         var model = TypeModel.Create();

         var orig = new TypeWithReferenceList(Enumerable.Empty<SomeReferenceType>());         
         var clone = (TypeWithReferenceList)model.DeepClone(orig);
         Assert.IsNotNull(clone.List);
         Assert.IsEmpty(clone.List);         
      }

      [Test]
      public void TestNullList()
      {
         var model = TypeModel.Create();
         var orig = new TypeWithReferenceList(null);         
         var clone = (TypeWithReferenceList)model.DeepClone(orig);
         Assert.IsNull(clone.List);
      }

      [Test]
      public void TestList()
      {
         var model = TypeModel.Create();
         model[typeof (SomeReferenceType)].AsReferenceDefault = true;

         SomeReferenceType repeatedItem = new SomeReferenceType(123);
         var orig = new TypeWithReferenceList(new []{repeatedItem, repeatedItem});
         var clone = (TypeWithReferenceList)model.DeepClone(orig);

         Assert.AreEqual(orig.List.Count, clone.List.Count);
         Assert.AreSame(orig.List[0], orig.List[1]);

         Assert.AreEqual(orig.List[0].Value, clone.List[0].Value);
         Assert.AreSame(clone.List[0], clone.List[1]);            
      }
   }

   [ProtoContract(ImplicitFields = ImplicitFields.AllFields, SkipConstructor = true)]
   public class SomeReferenceType
   {
      private int value;
      public SomeReferenceType(int val)
      {
         value = val;
      }
      public int Value { get { return value; } }
   }

   [ProtoContract(ImplicitFields = ImplicitFields.AllFields, SkipConstructor = true)]
   public class TypeWithReferenceList
   {
      private List<SomeReferenceType> innerList;

      public TypeWithReferenceList(IEnumerable<SomeReferenceType> items)
      {
         innerList = items == null ? null : items.ToList();
      }

      public List<SomeReferenceType> List { get { return innerList; } }
   }

}

Upvotes: 1

Views: 602

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062502

The third appears to be a glitch applying AsReferenceDefault to implicit fields; it looks like decorating the member manually (with AsReference=true) works, as does applying it at runtime:

model[typeof(TypeWithReferenceList)][1].AsReference = true;

I will investigate why this is.

The second already passes due to the way protobuf skips over nulls by default.

The first is trickier - again, since the google spec has no concept of null, this is ... problematic. It can currently be spoofed with an annoying couple of fake properties, but not ideal. Personally, I would say "keep it simple; make the collections always non null" (usually via either a field initializer or a deserialization callback). It is also possible that a recent change to allow nulls inside lists can be extended to support explicit nulls outside lists (on an opt-in basis).

The problem here, however, seems to be that you want it to behave like BinaryFormatter. However, it isn't BinaryFormatter. It is not designed as a drop-in replacement for BinaryFormatter (although it can do a good job replacing XmlSerializer or DataContractSerializer, which have more similar semantics). The underlying wire-format ultimately has limitations - this being the manifestation of a wire format designed by Google to be efficient and fairly baggage-free.

In particular, most of these "limitations" won't appear when dealing with most DTO models and target scenarios.

Upvotes: 1

Related Questions