Defining Immutable Objects - jamescourtney/FlatSharp GitHub Wiki

FlatSharp does its best to support creating immutable objects, or immutable properties within objects. Immutability is a useful programming practice in some scenarios, so FlatSharp does its best to help.

Deserialization Modes

Most deserialized items in FlatSharp are immutable. The exception is objects deserialized using the GreedyMutable option. All other options will throw a NotMutableException when trying to modify a deserialized object, with the exception of WriteThrough.

Defining Immutable Tables and Structs

FlatSharp supports defining immutable tables and structs, using a few different patterns:

Init-Only Properties (C# 9 and above)

If you're using C# 9, FlatSharp versions 5 and above support Init Only properties declared with the following patterns. This is the preferred way to make properties immutable in C# 9.

// FBS annotation: fs_setter:"Init"
public virtual string MyProperty { get; init; }

// FBS annotation: fs_setter:"ProtectedInit"
public virtual string MyProperty { get; protected init; }

// FBS annotation: fs_setter:"ProtectedInternalInit"
public virtual string MyProperty { get; protected internal init; }

Required Properties (C# 11)

If you're using FlatSharp 7 and C# 11 in your projects, FlatSharp will emit the required modifier for all properties marked as such in the FBS definition. This causes compiler errors if such properties are not initialized. Creating immutable objects is trivial with both required and init properties, because they are: only settable at initialization and required to be set at initialization.

Non-Public Setters

If you're still on C# 8 and don't have access to init-only properties, then you can use non-public or setter-free properties:

// FBS annotation: fs_setter:"None"
public virtual string MyProperty { get; }

// FBS annotation: fs_setter:"Protected"
public virtual string MyProperty { get; protected set; }

// FBS annotation: fs_setter:"ProtectedInternal"
public virtual string MyProperty { get; protected internal set; }

Constructor Options

Beginning in version 5, FlatSharp does not require types to expose default constructors. This behavior is useful if you wish to define your own constructors so that objects are never instantiated the wrong way. FlatSharp supports the fs_defaultCtor` attribute, which controls how default constructors are generated.

Full Example (FBS)

An FBS file for the example above looks like:

attribute "fs_defaultCtor";
attribute "fs_setter";

table Person (fs_defaultCtor:"None")
{
   Name:string (fs_setter:"None");
   Location:Location (fs_setter:"None");
}

// Structs in FBS files require a default constructor, but we declare it to be obsolete in this case
// to trigger a compiler warning if it is used.
struct Location (fs_defaultCtor:"PublicObsolete")
{
   X:float (fs_setter:"Protected");
   Y:float (fs_setter:"Protected");
   Z:float (fs_setter:"Protected");
}

These classes don't have default constructors, so we need to add them in C#. FlatSharp generates all code as partial, so you are free to add constructors to FlatSharp's definitions:

public partial class Person
{   
   public Person(string name, Location location)
   {
      this.Name = name;
      this.Location = location;
   }
}

public partial class Location
{
   public Location(float x, float y, float z)
   {
        this.X = x;
        this.Y = y;
        this.Z = z;
   }
}