1
votes

Given the following code:

class Data
{
    public List<int> numbers = new List<int>();
}

Where MetaType.UseConstructor is set to false (in order or avoid creating an empty constructor), I'd like to find a way to actually store the empty container. I'm aware that protobuf-net is going to correctly serialize/deserialize if the list has at least one item, but if the list is empty, I got a null reference when deserializing. Of course its resolved by using constructors, but while integrating protobuf-net to a large codebase, having to create an empty constructor for each class is super hard and creates ambiguities for constructors with all-optional parameters.

Can protobuf-net correctly store a valid reference to an empty container?

1
What about using a property to lazy-initialize the list? (In the getter, checking to see if it's null and constructing the list then.) - cdhowie
Can you clarify "having to create an empty constructor for each class is super hard" please? You know, there is always a constructor. - Sinatr
For example, if you have 200+ classes, and most of them are only constructed with parameters (intended), you are obligated to write a new parameterless constructor for each one, and also, you may silently introduce the side effect of allowing to create an object without the proper parameters (which may be an invalid object). - Federico Oro Vojacek
@cdhowie I'm trying to get to a solution where there is minimal change in the codebase and minimal requirements so programmers cannot infringe the serialization model easily. - Federico Oro Vojacek

1 Answers

1
votes

The protobuf wire format has no concept of containers; just elements. So in terms of data, no there is nothing whatsover from which to do this.

From the discussion on constructors, I'm guessing that you have enabled constructor-skipping, for example:

[ProtoContract(SkipConstructor=true)]

In that case, indeed: field-initializers will not be invoked, and protobuf-net will not initialize fields that it doesn't see in the data. So in the case of an empty list, yes it will result in a null. That shouldn't actually cause any errors during deserialization, since protobuf-net will initialize the list if it has a need to touch it - but yes, I can understand that some other code could touch the field.

Adding a parameterless constructor and removing the SkipConstructor would allow the existing field-initializers to run; for example:

[Obsolete("For serialization purposes only")]
private void Data() {}

Another option would be to use a deserialization callback, i.e.

[ProtoBeforeDeserialize]
private void Init() {
    numbers = new List<int>();
}

although this adds work (we now need to remember all the fields with field-initializers)