unit Neon.Core.Nullables; interface uses System.SysUtils, System.Variants, System.Classes, System.Generics.Defaults, System.Rtti, System.TypInfo, System.JSON; type ENullableException = class(Exception); {$RTTI EXPLICIT FIELDS([vcPrivate]) METHODS([vcPrivate])} Nullable = record private FValue: T; FHasValue: string; procedure Clear; function GetValueType: PTypeInfo; function GetValue: T; procedure SetValue(const AValue: T); function GetHasValue: Boolean; public constructor Create(const Value: T); overload; constructor Create(const Value: Variant); overload; function Equals(const Value: Nullable): Boolean; function GetValueOrDefault: T; overload; function GetValueOrDefault(const Default: T): T; overload; property HasValue: Boolean read GetHasValue; function IsNull: Boolean; property Value: T read GetValue; class operator Implicit(const Value: Nullable): T; class operator Implicit(const Value: Nullable): Variant; class operator Implicit(const Value: Pointer): Nullable; class operator Implicit(const Value: T): Nullable; class operator Implicit(const Value: Variant): Nullable; class operator Equal(const Left, Right: Nullable): Boolean; class operator NotEqual(const Left, Right: Nullable): Boolean; end; NullString = Nullable; NullBoolean = Nullable; NullInteger = Nullable; NullInt64 = Nullable; NullDouble = Nullable; NullDateTime = Nullable; implementation uses Neon.Core.Utils; { Nullable } constructor Nullable.Create(const Value: T); var a: TValue; begin FValue := Value; FHasValue := DefaultTrueBoolStr; end; constructor Nullable.Create(const Value: Variant); begin if not VarIsNull(Value) and not VarIsEmpty(Value) then Create(TValue.FromVariant(Value).AsType) else Clear; end; procedure Nullable.Clear; begin FValue := Default(T); FHasValue := ''; end; function Nullable.Equals(const Value: Nullable): Boolean; begin if HasValue and Value.HasValue then Result := TEqualityComparer.Default.Equals(Self.Value, Value.Value) else Result := HasValue = Value.HasValue; end; function Nullable.GetHasValue: Boolean; begin Result := FHasValue <> ''; end; function Nullable.GetValueType: PTypeInfo; begin Result := TypeInfo(T); end; function Nullable.GetValue: T; begin if not HasValue then raise ENullableException.Create('Nullable type has no value'); Result := FValue; end; function Nullable.GetValueOrDefault(const Default: T): T; begin if HasValue then Result := FValue else Result := Default; end; function Nullable.GetValueOrDefault: T; begin Result := GetValueOrDefault(Default(T)); end; class operator Nullable.Implicit(const Value: Nullable): T; begin Result := Value.Value; end; class operator Nullable.Implicit(const Value: Nullable): Variant; begin if Value.HasValue then Result := TValue.From(Value.Value).AsVariant else Result := Null; end; class operator Nullable.Implicit(const Value: Pointer): Nullable; begin if Value = nil then Result.Clear else Result := Nullable.Create(T(Value^)); end; class operator Nullable.Implicit(const Value: T): Nullable; begin Result := Nullable.Create(Value); end; class operator Nullable.Implicit(const Value: Variant): Nullable; begin Result := Nullable.Create(Value); end; function Nullable.IsNull: Boolean; begin Result := FHasValue = ''; end; class operator Nullable.Equal(const Left, Right: Nullable): Boolean; begin Result := Left.Equals(Right); end; class operator Nullable.NotEqual(const Left, Right: Nullable): Boolean; begin Result := not Left.Equals(Right); end; procedure Nullable.SetValue(const AValue: T); begin FValue := AValue; FHasValue := DefaultTrueBoolStr; end; end.