Use Nullable Values
Preserve absence as data without allocating an object or inventing a sentinel.
Goal and component
Represent a value that may be absent without allocating an object or using a sentinel. Nullable<T> belongs to Core.Base and wraps std::optional<T>.
set(SHARP_RUNTIME_COMPONENTS Core.Base)
target_link_libraries(app PRIVATE SharpRuntime::Core.Base)
Create present and absent values
#include <System/Nullable.hpp>
System::Nullable<int> missing;
System::Nullable<int> score = 42;
if (!missing.getHasValueProperty() &&
score.getHasValueProperty()) {
const int value = score.getValueProperty();
(void)value;
}
A default value is absent. Construction from T is intentionally implicit, mirroring the convenient C# conversion from T to T?. getValueProperty() returns a const reference and throws InvalidOperationException when absent.
Choose a fallback
const int defaultLimit = missing.GetValueOrDefault(); // int{} == 0
const int configured = missing.GetValueOrDefault(100); // explicit policy
Prefer the explicit overload when the domain fallback is not the type’s default. Do not translate every C# ?? into zero or empty text by convention.
Equality has a floating-point nuance
Nullable::Equals uses the runtime’s default-equality policy, which treats two floating NaNs as equal and supplies a matching hash. operator== models C# lifted == and uses the underlying operator, so two nullable NaNs compare false. That asymmetry is deliberate and tested.
Nullable is not pointer ownership
| Need | Use |
|---|---|
| Optional small/value type | Nullable<T> or std::optional<T> |
| Optional exclusive object | std::unique_ptr<T> |
| Observer that may expire | std::weak_ptr<T> |
| Borrow that is either present or absent | Documented raw pointer |
The contained T still controls copy, move, destruction, and cost. Avoid Nullable<unique_ptr<T>> unless three states are genuinely required.
Verify the edge cases
- Absent
Valuethrows the expected Sharp Runtime exception. - Fallback is evaluated according to your own control flow.
- Copy/move behavior of a resource-owning
Tis acceptable. - Equality and hash policy match the dictionary/set comparer that consumes it.