-
Notifications
You must be signed in to change notification settings - Fork 130
Freezable Pattern
The Freezable Pattern helps to prevent Race Conditions in asynchronous code. It defines an object that can be changed into an immutable (frozen) state. It is save to pass this object to other threads when it is in the frozen state.
The object’s internal state can be modified until it is marked as frozen. When the object is frozen then it stays in this immutable state as long the object lives. It is not possible to unfreeze the object so that the internal state can be modified again. If a writeable version of a frozen object is needed then a common approach is to create a clone of it.
The following code snippet shows an example implementation for a base class that can be used to create freezable classes. All derived classes have to ensure that they call first the ThrowIfFrozen
method before they modify the object’s internal state. This ensures that if some flawed code tries to modify a frozen object then an exception will be thrown.
public abstract class Freezable
{
private volatile bool isFrozen;
public bool IsFrozen { get { return isFrozen; } }
public void Freeze()
{
isFrozen = true;
}
protected bool SetProperty<T>(ref T field, T value)
{
ThrowIfFrozen();
if (object.Equals(field, value)) { return false; }
field = value;
return true;
}
protected void ThrowIfFrozen()
{
if (isFrozen)
{
throw new InvalidOperationException(
"The object is frozen and must not be modified anymore.");
}
}
}
The SetProperty
method can be used in the property setter implementation. It calls first the ThrowIfFrozen
method and then sets the internal field if the value has changed. The usage of this method is shown in the next code snippet.
public class Person : Freezable
{
private string name;
public double Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
...
The pattern should be used with care because the implementation must ensure that first the freeze state is checked before the object’s state is going to be changed. Furthermore, the code is responsible that the object is put into the frozen state before it is passed to other threads.
Microsoft uses this pattern in WPF. Examples of freezable objects include brushes, pens, transformations, geometries, and animations.
- Data Transfer Object (DTO)