Temporal Nulls in C#
-
While Nullable Reference Types are great, in some ways they just kick the problem down the road. In a workflow an object may allow a field to be null in an early step, but mandatory in a later step. I call these `Temporal Nulls`. If we want our objects to accurately reflect the business case/state NRT's introduce a new design decision. How are you approaching this? Explainer In Step 1 I am gathering data. Assume I am not creating the object here, just adding `Info` data. In Step 2 I am using the data. At this point `Info` is required data. The object is the same object from a business perspective, but from a technology perspective it has two states. ``` // Step 1 State class MyInfo { public InfoData? Info; } // Step 2 State class MyInfo { public InfoData Info; } ``` I can think of three general approaches to this. 1. Use multiple classes 2. Use properties or multiple fields 3. Use defaults The multiple class approach (above) is the most "accurate" but multiplies maintenance and would require different names for the classes (e.g., MyInfo & MyInfoWip). A property or multiple field approach can look something like this ``` class MyInfo { private _infoData?; public InfoData { get => _infoData ?? throw ... set => _infoData = value } } ``` The drawback here is that I've basically exchange a null for an error and I don't know until I try to use it. The defaults approach is getting common using `Empty` ``` class MyInfo { public InfoData = InfoData.Empty; public static InfoData Empty => new InfoData(); } ``` This is just kicking the can. I've replaced a null check with a "Empty Check". It requires defining what `Empty` values required fields get - `new InfoData(emptyValues)`. What other approaches come to mind, and how do you normally deal with Temporal Nulls?
-
While Nullable Reference Types are great, in some ways they just kick the problem down the road. In a workflow an object may allow a field to be null in an early step, but mandatory in a later step. I call these `Temporal Nulls`. If we want our objects to accurately reflect the business case/state NRT's introduce a new design decision. How are you approaching this? Explainer In Step 1 I am gathering data. Assume I am not creating the object here, just adding `Info` data. In Step 2 I am using the data. At this point `Info` is required data. The object is the same object from a business perspective, but from a technology perspective it has two states. ``` // Step 1 State class MyInfo { public InfoData? Info; } // Step 2 State class MyInfo { public InfoData Info; } ``` I can think of three general approaches to this. 1. Use multiple classes 2. Use properties or multiple fields 3. Use defaults The multiple class approach (above) is the most "accurate" but multiplies maintenance and would require different names for the classes (e.g., MyInfo & MyInfoWip). A property or multiple field approach can look something like this ``` class MyInfo { private _infoData?; public InfoData { get => _infoData ?? throw ... set => _infoData = value } } ``` The drawback here is that I've basically exchange a null for an error and I don't know until I try to use it. The defaults approach is getting common using `Empty` ``` class MyInfo { public InfoData = InfoData.Empty; public static InfoData Empty => new InfoData(); } ``` This is just kicking the can. I've replaced a null check with a "Empty Check". It requires defining what `Empty` values required fields get - `new InfoData(emptyValues)`. What other approaches come to mind, and how do you normally deal with Temporal Nulls?
private string _info = null;
public string Info { get { return _info ?? "I'm empty"; } set { _info = value; } }It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it. ― Confucian Analects: Rules of Confucius about his food
-
While Nullable Reference Types are great, in some ways they just kick the problem down the road. In a workflow an object may allow a field to be null in an early step, but mandatory in a later step. I call these `Temporal Nulls`. If we want our objects to accurately reflect the business case/state NRT's introduce a new design decision. How are you approaching this? Explainer In Step 1 I am gathering data. Assume I am not creating the object here, just adding `Info` data. In Step 2 I am using the data. At this point `Info` is required data. The object is the same object from a business perspective, but from a technology perspective it has two states. ``` // Step 1 State class MyInfo { public InfoData? Info; } // Step 2 State class MyInfo { public InfoData Info; } ``` I can think of three general approaches to this. 1. Use multiple classes 2. Use properties or multiple fields 3. Use defaults The multiple class approach (above) is the most "accurate" but multiplies maintenance and would require different names for the classes (e.g., MyInfo & MyInfoWip). A property or multiple field approach can look something like this ``` class MyInfo { private _infoData?; public InfoData { get => _infoData ?? throw ... set => _infoData = value } } ``` The drawback here is that I've basically exchange a null for an error and I don't know until I try to use it. The defaults approach is getting common using `Empty` ``` class MyInfo { public InfoData = InfoData.Empty; public static InfoData Empty => new InfoData(); } ``` This is just kicking the can. I've replaced a null check with a "Empty Check". It requires defining what `Empty` values required fields get - `new InfoData(emptyValues)`. What other approaches come to mind, and how do you normally deal with Temporal Nulls?
Depending how you intend to populate it, this sounds like a decent use of Lazy properties. Getting Lazy in C# - Kill All Defects[^]
-
While Nullable Reference Types are great, in some ways they just kick the problem down the road. In a workflow an object may allow a field to be null in an early step, but mandatory in a later step. I call these `Temporal Nulls`. If we want our objects to accurately reflect the business case/state NRT's introduce a new design decision. How are you approaching this? Explainer In Step 1 I am gathering data. Assume I am not creating the object here, just adding `Info` data. In Step 2 I am using the data. At this point `Info` is required data. The object is the same object from a business perspective, but from a technology perspective it has two states. ``` // Step 1 State class MyInfo { public InfoData? Info; } // Step 2 State class MyInfo { public InfoData Info; } ``` I can think of three general approaches to this. 1. Use multiple classes 2. Use properties or multiple fields 3. Use defaults The multiple class approach (above) is the most "accurate" but multiplies maintenance and would require different names for the classes (e.g., MyInfo & MyInfoWip). A property or multiple field approach can look something like this ``` class MyInfo { private _infoData?; public InfoData { get => _infoData ?? throw ... set => _infoData = value } } ``` The drawback here is that I've basically exchange a null for an error and I don't know until I try to use it. The defaults approach is getting common using `Empty` ``` class MyInfo { public InfoData = InfoData.Empty; public static InfoData Empty => new InfoData(); } ``` This is just kicking the can. I've replaced a null check with a "Empty Check". It requires defining what `Empty` values required fields get - `new InfoData(emptyValues)`. What other approaches come to mind, and how do you normally deal with Temporal Nulls?
I suggest you look at the new static analysis features/attributes in C# 8: [^]
Quote:
AllowNull: A non-nullable input argument may be null. DisallowNull: A nullable input argument should never be null. MaybeNull: A non-nullable return value may be null. NotNull: A nullable return value will never be null. MaybeNullWhen: A non-nullable input argument may be null when the method returns the specified bool value. NotNullWhen: A nullable input argument will not be null when the method returns the specified bool value. NotNullIfNotNull: A return value isn't null if the argument for the specified parameter isn't null. DoesNotReturn: A method never returns. In other words, it always throws an exception. DoesNotReturnIf: This method never returns if the associated bool parameter has the specified value.
I recommend Jonathan Allen's coverage of these features on InfoQ: >[^]
«One day it will have to be officially admitted that what we have christened reality is an even greater illusion than the world of dreams.» Salvador Dali