3

Say I have a class workStockItem, that I wish to serialize.

Which is the better style?

  1. using a static method

    or

  2. using a non-static method (maybe even implemented as a property as seen below)

Example implementations:

internal static byte[] serialiaze(workStockItem toSer)
    {
        IFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        formatter.Serialize(stream, toSer);
        return stream.ToArray();
    }

internal byte[] binaryRepresentation
    {
        get
        {
            IFormatter formatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            formatter.Serialize(stream, this);
            return stream.ToArray();
        }
    }

Again:
Which is better style and why?

  • 2
    More of a Java programmer than C#, but deserialization should be a static method (so you don't have to initialize an instance before recovering the serialized one). Then, if only for consistence, I would make serialize static. – SJuan76 Jul 4 '14 at 7:08
  • @SJuan76 actually deserialization will be workStockItem foo = (workStockItem)new IFormatter().deserialize(incomingByteStream) so your point does not fully apply. However instance methods should have to be dependent on instance data, which it is not here, so static is still the thing to do. Thanks. – Mark Jul 4 '14 at 7:35
  • 1
    @Mark There is nothing stopping you from making a non-static version that simply returns the static serialize call, if that's what you prefer. Honestly it would bother me too to pass an instance to a static method within its own class, but I do think the actual work should be done in the static method. – Neil Jul 4 '14 at 7:42
  • @Neil maybe calling the static method from within the non-static one? cringes ;o) I have decided for the static one now and will implement a static method for deserialization, in case I ever need it and for completeness – Mark Jul 4 '14 at 7:45
5

tl;dr

I suggest static methods, or even instance methods can be alright depending on the objects you're working with and how long lived they are as well as how stateful they are.

Do not use properties for this. Properties should be as idempotent as possible (repeated access should return the same result constantly). If A property needs to execute logic to divine its state, that's not always terrible, but properties should never be changing state on a Get.

Usually when you're dealing with an underlying set of data to deserialize something you'll have a current position in that set, and the act of deserializing will move that position thus causing a Get to change state.


I once wrote a bunch of binary serialization for some simple objects we had, and found it quite helpful to just make the methods extension methods on the BinaryReader and BinaryWriter classes. If you look at these classes, they already have methods for turning binary data into data types with ReadInt and ReadString etc, so the static methods relied on these to reconstruct the objects.

It as as such:

public class FunnyAnimal
{
    public int NumberOfLegs { get; set; }
    public string Name { get; set; }
    public Tuple<int, int> EarHearingSensitivity { get; set; }
}

public static class BinaryWriterExtensions
{
    public FunnyAnimal WriteFunnyAnimal(this BinaryWriter writer, FunnyAnimal animalToBeWritten)
    {
        writer.WriteInt(animalToBeWritten.NumberOfLegs);
        writer.WrinteString(animalToBeWritten.Name);
        writer.WriteInt(animalToBeWritten.EarHearingSensitivity.Item1);
        writer.WriteInt(animalToBeWritten.EarHearingSensitivity.Item2);
    }
}

public static class BinaryReaderExtensions
{
    public FunnyAnimal ReadFunnyAnimal(this BinaryReader reader)
    {
        return new FunnyAnimal()
        {
            NumberOfLegs = reader.ReadInt(),
            Name = reader.ReadString(),
            EarHearingSensitivity = Tuple.Create(reader.ReadInt(), reader.ReadInt())
        };
    }
}

Then the use is:

//To write it out to a stream
using (var writer = new BinaryWriter(someUnderlyingStreamToWriteTo))
{
    writer.WriteFunnyAnimal(yourFunnyAnimalObject);
}

//To read it in from a stream
FunnyAnimal deserializedFunnyAnimal = null;
using (var reader = new BinaryReader(someUnderlyingStreamToReadFrom))
{
    deserializedFunnyAnimal = reader.ReadFunnyAnimal();
}

The same approach could be applied to any type of serializer you're working with, if you're just using straight BinaryFormatter you can make your extensions on a MemoryStream and make the methods utilize a BinaryFormatter to read and write from the stream, but the use is then clear and simple, construct a MemoryStream and ReadFunnyAnimal or WriteFunnyAnimal on it, and the extension methods will create and operate the BinaryFormatter


Last thoughts

When dealing with binary serialization/deserialization you'll find the most convenient stream underlying a lot of times may just be a MemoryStream with an array backing it so you can just grab the byte array when you're done, unless you have a file or other resource you're specifically serializing across.

If you have complex objects inside that need serializing, this technique is helpful because the implementation for the writer and reader extensions will have access to the reader/writer so the extension methods for your embedded complex objects will also be in scope to be used as:

return new FunnyAnimal() { ComplexTailMember = reader.ReadTail(); }

Additionally it's worth recognizing when dealing with serialization/deserialization implementations who's going to own the stream. Your extensions shouldn't own it in a sense that they should not open/move/dispose of it after they're done, rather that should be on the ownus of the code that creates the reader/writer, so rule of thumb dealing with streams and streamreader/writers:

If your Stream/StreamReader/StreamWriter are parameters to your method, you should not dispose of them. If you create the Stream it's your responsibility to dispose of it.

The one sniglet to be careful of there is sometimes a StreamReader/StreamWriter's dispose method will dispose of the underlying stream, just beware that you don't therefore dispose of your reader/writer before you are completely finished with your stream.

  • his property implementation didn't change the object in any way, it just passed itself into a formatter and returned the resulting bytes – ratchet freak Jul 4 '14 at 10:27
  • @ratchetfreak yeah I noticed, I was speaking generally - that's the general rule of why to avoid such things. His particular implementation doesn't break that rule though good point. I suppose I should just mention it violates POLA but I consider getters changing state a much bigger problem so wanted to detail that. – Jimmy Hoffa Jul 4 '14 at 15:18
  • It may be worth noting that there is often no possible "non-surprising" behavior for a property which return a mutable object other than one which is supplied by and owned by its client. That in and of itself would be sufficient basis for opposing a property that returns a newly-constructed array. Calling code would be entitled to expect var v1=thing.prop; var v2=thing.prop; to be equivalent to var v1=thing.prop; var v2=v1;, and also to expect that after var v1=thing.prop;, thing wouldn't care if the caller exposed v1 to outside code. If the data in prop is owned by thing... – supercat Aug 29 '14 at 15:41
  • ...and the object is mutable, the second requirement could only be met by having the property return a new defensive copy of the data each time it's called; that behavior would violate the first requirement. – supercat Aug 29 '14 at 15:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.