-
-
Notifications
You must be signed in to change notification settings - Fork 51
Defining Immutable Objects
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.
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.
FlatSharp supports defining immutable tables and structs, using a few different patterns:
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; }
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.
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; }
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.
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;
}
}