Open Source  /  radNullableTypes

radNullableTypes

Focused, dependency-light nullable and tri-state value types for Delphi. A freshly declared value starts null — guaranteed, not stack garbage — lifts from a bare value implicitly, and only unwraps explicitly, so a null can never slip into ordinary code unnoticed.

MIT licensed Delphi 10.4 Sydney+ Pure RTL — no dependencies

Two value types, one clear contract

radNullableTypes gives Delphi the optional-value semantics that higher-level languages take for granted — without dragging heavyweight RTL units into every unit that merely declares an optional field. The core unit radNullable depends only on System.SysUtils, System.TypInfo, and System.Generics.Defaults; Variant / TValue / JSON conversions live in a separate radNullable.Rtti companion so they are opt-in.

TNullable<T>

Null or value

A two-state value — the Delphi equivalent of .NET Nullable<T>. Implicit in, explicit out: a bare value lifts automatically, but unwrapping back to T is explicit and raises if it is null. Lenient accessors (ValueOrDefault, TryGetValue) never raise.

TTriState<T>

Unset / null / value

A three-state value that distinguishes “field omitted” from “field explicitly null” — exactly what JSON Merge Patch and cloud update-to-clear (PATCH) semantics require.

Correct by construction

Managed record

Starts null, always

A custom Initialize operator guarantees a freshly declared local reads as null (or Unset) rather than stack garbage — the hard floor of Delphi 10.4 Sydney exists precisely to make this guarantee.

Total ordering

Usable as a key

Equality and ordering follow .NET Nullable<T>, not SQL three-valued logic: null = null is true and null sorts below every value. A ready-made cached comparer makes a nullable safe as a dictionary or sort key, even for managed T like string.

A freshly declared nullable starts null

uses radNullable;

var
  Age: TNullable<Integer>;
begin
  Assert(Age.IsNull);              // guaranteed, not stack garbage

  Age := 42;                       // implicit lift from a value
  if Age.HasValue then
    Writeln(Age.Value);            // 42

  Age := NullValue;                // back to null via the sentinel
  Writeln(Age.ValueOrDefault(-1)); // -1  (lenient, never raises)

  Age := 7;
  Writeln(Integer(Age));           // 7  -- explicit unwrap
end;

Get it

Add the source directory to your project search path and use the units. That is the whole install — there is nothing to build and nothing to link.

uses
  radNullable,        // TNullable<T>, TTriState<T>, comparer, NullValue
  radNullable.Rtti;   // Variant / TValue / JSON conversions (optional)