.Net Tips — making a serializable immutable struct

As you might know already, an object is immutable if its state doesn’t change once it has been cre­at­ed.

In C# the most used immutable type is string, this means every time you mod­i­fy the val­ue of a string vari­able you are actu­al­ly cre­at­ing a new string object and updat­ing the ref­er­ence of the vari­able to point to the new object.

Class vs Struct

When cre­at­ing a new type, you have the choice of either a class or a struct. The gen­er­al rule of thumb is to go with a class except for light­weight types small­er than 16 bytes in which case it is more effi­cient to use a struct. The rea­son a struct can be more effi­cient is because a struct is a val­ue type and there­fore goes straight onto the stack so we don’t have the over­head of hav­ing to hold the ref­er­ence to the object itself (4 bytes in a 32bit sys­tem).

Mutable vs Immutable

In addi­tion, you also have to con­sid­er whether your type should be muta­ble or immutable. In gen­er­al, a struct should always be immutable because a struct usu­al­ly rep­re­sents some fun­da­men­tal val­ue – such as the num­ber 5 – and whilst you can change a variable’s val­ue you don’t log­i­cal­ly change the val­ue itself.

Also, data loss is far too easy with muta­ble structs, con­sid­er the fol­low­ing:

Foo foo = new Foo(); // a mutable struct
foo.Bar = 27;
Foo foo2 = foo;
foo2.Bar = 55;

Now foo.Bar and foo2.Bar is dif­fer­ent, which is often unex­pect­ed.

Here are some of the advan­tages of using an immutable val­ue type:

  • Eas­i­er val­i­da­tion — if you val­i­date the para­me­ters used to con­struct your object, your object will nev­er be invalid as its state can nev­er be changed.
  • Thread safe­ty — immutable types are inher­ent­ly thread-safe because there is no chance for dif­fer­ent threads to see incon­sis­tent views of the same data if the data can nev­er be changed.
  • Bet­ter encap­su­la­tion — immutable types can be export­ed from your objects safe­ly because the caller can­not mod­i­fy the inter­nal state of your objects.
  • Bet­ter for hash-based col­lec­tions — the val­ue returned by Object.GetHashCode() must be an instance invari­ant, which is always true for immutable types.

Deserializing an Immutable Struct

To cre­ate an immutable struct, you usu­al­ly have no set­ters on prop­er­ties and in all like­li­hood the pri­vate vari­ables that the get­ters return will be made read­on­ly too to enforce the write-once rule. The lack of pub­lic set­ters on prop­er­ties, how­ev­er, rep­re­sents a chal­lenge when serializing/deserializing the immutable structs.

The eas­i­est way to get around this in my expe­ri­ence is to sim­ply imple­ment the ISe­ri­al­iz­able inter­face and pro­vid­ing a con­struc­tor which takes a Seri­al­iza­tion­In­fo and a Stream­ing­Con­text object:

[Serializable]
public struct MyStruct: ISerializable
{
    private readonly int _x;
    private readonly int _y;

    // normal constructor
    public MyStruct(int x, int y) : this()
    {
        _x = x;
        _y = y;
    }

    // this constructor is used for deserialization
    public MyStruct(SerializationInfo info, StreamingContext text) : this()
    {
        _x = info.GetInt32("X");
        _y = info.GetInt32("Y");
    }

    public int X { get { return _x; } }
    public int Y { get { return _y; } }

    // this method is called during serialization
    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("X", X);
        info.AddValue("Z", Y);
    }
}

Reference:

Stack­Over­flow thread on immutabil­i­ty of structs

Patrick Smacchia’s arti­cle on Immutable Types: under­stand their ben­e­fits and use them

Eric Lip­pert’s series on immutabil­i­ty in C#:

Immutabil­i­ty in C# Part One: Kinds of Immutabil­i­ty

Immutabil­i­ty in C# Part Two: A Sim­ple Immutable Stack

Immutabil­i­ty in C# Part Three: A Covari­ant Immutable Stack

Immutabil­i­ty in C# Part Four: An Immutable Queue

Immutabil­i­ty in C# Part Five: LOLZ!

Immutabil­i­ty in C# Part Six: A Sim­ple Bina­ry Tree

Immutabil­i­ty in C# Part Sev­en: More on Bina­ry Trees

Immutabil­i­ty in C# Part Eight: Even More On Bina­ry Trees

Immutabil­i­ty in C# Part Nine: Aca­d­e­m­ic? Plus my AVL tree imple­men­ta­tion

Immutabil­i­ty in C# Part Ten: A dou­ble-end­ed queue

Immutabil­i­ty in C# Part Eleven: A work­ing dou­ble-end­ed queue

Luca Bolog­nese’s series on imple­ment­ing immutable val­ue objects:

Cre­at­ing an immutable val­ue object in C# — Part I — Using a class

Cre­at­ing an immutable val­ue object in C# — Part II — Mak­ing the class bet­ter

Cre­at­ing an immutable val­ue object in C# — Part III — Using a struct

Cre­at­ing an immutable val­ue object in C# — Part IV — A class with a spe­cial val­ue

Cre­at­ing an immutable val­ue object in C# — Part V — Using a library