Introduction
While learning the Nullable<T>
class, I found the same logic can be used to create objects which have the capability to check its internal state itself and
correct if it is invalid. The Nullable
class is just a wrapper class to keep a generic type value internally. So I decided to create a generic wrapper class to
monitor its internal value.
Using the Code
Below is the complete code for StateCheck
class.
public class StateCheck<T>
{
private T value;
private DefaultValueAction DefaultValue;
private CheckStateAction CheckState;
public delegate T DefaultValueAction(T current,T previous);
public delegate bool CheckStateAction(T current, T previous);
public StateCheck(CheckStateAction checkState, DefaultValueAction defaultValue)
{
CheckState = checkState;
DefaultValue = defaultValue;
}
public T Value()
{
return value;
}
public void Value(T val)
{
value = !CheckState(val, value) ? DefaultValue(val, value) : val;
}
}
The StateCheck
class is a simple generic class which accepts any data types as its generic type. There is a private variable named value
which is
used to store the actual value of the generic type. The class has only one parameterized constructor which accepts two functions as its parameters. These functions have a key
role on this logic. The user can define both of these functions at the time of object creation.
Both functions can access the previous value (the value already assigned to the value
variable) and new incoming value as parameters. The first function is a
validation function which may return false if the new incoming value is invalid. If the validation function returns false, then the second function will execute. The purpose of
this function is to provide a default value and prevent the object from being in an invalid state. These functions give flexibility to the developers to define the behavior of
an individual instance of StateCheck
class whenever it meets an invalid value. It is impossible to declare a StateCheck
class object without specifying
both the functions. This will ensure the functions are available before assigning any values.
The remaining two overloaded public functions are used to get and set the value. The parameter less Value
function simply returns the value. The second function
used to set the value. This function contains the logic which makes sure the StateCheck
class object doesn’t accept any invalid values. Before assigning, it will
check whether the input is invalid. If it is invalid, the DefaultValue
delegate will be executed.
Now we will check how to use this class. To do this, just create a console application and copy the code below in to the main function.
StateCheck<string> str = new StateCheck<string>(
(current, previous) => { return current != null; },
(current, previous) => { return "EMPTY"; });
str.Value("test string");
Console.WriteLine(str.Value());
str.Value(null);
Console.WriteLine(str.Value());
The str
variable is designed to fill with the word “EMPTY” when it is assigned with a null value. In the beginning, the variable assigned with the string
“test string”. This is a valid string based on the condition we given at the time of object creation. So it will return the same when Console.WriteLine
first time tried
to fetch the value. In the next step, the variable assigned with a null value which is an invalid string based on our definition. So when the second time
Console.WriteLine
tried to fetch the value, the variable return the default value (“EMPTY”).
Null object pattern
The Null object pattern is a design pattern to avoid null objects. The core idea behind this design pattern is that to use a dummy object instead of a null value. So whenever
developer wants to assign a null value into the object, they will assign a dummy object instead. While using Null object pattern, the developer must ensure to create the dummy
object whenever there is a null value. How we can prevent an object is being assigned with a null value accidently instead of the dummy object? We can solve this issue using our
StateCheck
class. Here is the Wikipedia link if you want to know more about Null
object pattern. Below code is using the C# implementation of Null object pattern described in the above Wikipedia link.
To create the object of IAnimal
, we can use StateCheck
class as below.
StateCheck<IAnimal> animal = new StateCheck<IAnimal>(
(current, previous) => { return current != null; },
(current, previous) => { return new NullAnimal(); });
So whenever the animal
object assigns with a null value, the StateCheck
wrapper class will ensure it will assign the NullAnimal
object
instead. This will reduce the burden to ensure the object is not assigned with a null from anywhere in the program.
Advantages
You can avoid all the validation checks whether the object is in an invalid state. The Value
function is always grantee to return a valid data of the type
T
.
Drawbacks
The State checking wrapper class has its own drawbacks. Use it in only the places where you really want it. You should have few things in mind while using this.
- It requires memory - The additional wrapper class instance for every variable need additional memory.
- The main use of this method may avoid the null values. But the null values aren’t that evil. It has some specific purpose and meaning. The presence of null value should be
handled. Sometimes hiding a null reference exception and continue program execution will cause unpredictable results on application.
- What if the developer returns an invalid value from the default value delegated function? It is almost impossible to validate this. So I leave this risk to the developers.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.