I created my own Optional type some time ago and used management operators to initialize it (Mode Delphi):
type
TEmptyOptional = record;
{ TOptional }
TOptional<T> = record
public type
PT = ^T;
private
FHasValue: Boolean;
FValue: T;
class operator Initialize(var a: TOptional<T>);
public
constructor Create(constref AValue: T);
function ValuePtr: PT; inline;
function Value: T; inline;
function GetOrDefault(constref def: T): T;
class operator Implicit(constref AValue: T): TOptional<T>; inline;
class operator Explicit(constref AValue: T): TOptional<T>; inline;
class operator Implicit(constref AValue: TEmptyOptional): TOptional<T>; inline;
class operator Explicit(constref AValue: TEmptyOptional): TOptional<T>; inline;
class operator Implicit(constref opt: TOptional<T>): Boolean; inline;
class operator Explicit(constref opt: TOptional<T>): Boolean; inline;
class operator LogicalNot(constref opt: TOptional<T>): Boolean; inline;
property HasValue: Boolean read FHasValue;
class function Empty: TOptional<T>; static; inline;
end;
function EmptyOptional: TEmptyOptional; inline;
implementation
{ TOptional }
class operator TOptional<T>.Initialize(var a: TOptional<T>);
begin
a.FHasValue := False;
end;
constructor TOptional<T>.Create(constref AValue: T);
begin
FHasValue := True;
FValue := AValue;
end;
function TOptional<T>.ValuePtr: PT;
begin
If not HasValue then
raise ENoValuePresentException.Create('Trying to access value of an empty optional');
Result := @FValue;
end;
function TOptional<T>.Value: T;
begin
If not HasValue then
raise ENoValuePresentException.Create('Trying to access value of an empty optional');
Result := FValue;
end;
function TOptional<T>.GetOrDefault(constref def: T): T;
begin
If HasValue then
Result := FValue
else
Result := def;
end;
class operator TOptional<T>.Implicit(constref AValue: T): TOptional<T>;
begin
Result.FHasValue := True;
Result.FValue := AValue;
end;
class operator TOptional<T>.Explicit(constref AValue: T): TOptional<T>;
begin
Result.FHasValue := True;
Result.FValue := AValue;
end;
class operator TOptional<T>.Implicit(constref AValue: TEmptyOptional): TOptional
<T>;
begin
Result.FHasValue := False;
end;
class operator TOptional<T>.Explicit(constref AValue: TEmptyOptional): TOptional
<T>;
begin
Result.FHasValue := False;
end;
class operator TOptional<T>.Implicit(constref opt: TOptional<T>): Boolean;
begin
Result := opt.HasValue;
end;
class operator TOptional<T>.Explicit(constref opt: TOptional<T>): Boolean;
begin
Result := opt.HasValue;
end;
class operator TOptional<T>.LogicalNot(constref opt: TOptional<T>): Boolean;
begin
Result := not opt.HasValue;
end;
class function TOptional<T>.Empty: TOptional<T>;
begin
Result.FHasValue := False;
end;
function EmptyOptional: TEmptyOptional;
begin
// Nothing to do here
end;
The main difference to the TNullable example (except for the management initialization) is that it does not take pointers to be nullable, as this requires a runtime check, but can be cleared by using a custom dummy type TEmptyOptional, which is generated via the function emptyOptional, i.e.:
It also has some more casts that make it's usage easier (like implicit cast to bool to check for it's existance)
If you want to make that type single assignment readonly you can do the following:
1. remove the ValuePtr function, as this allows writing to the value without our access.
2. Implement the copy class operator as follows:
class operator TOptional<T>.Copy(constref aSrc: TOptional<T>;
var aDst: TOptional<T>);
begin
if aDst then
raise EAlreadyAssignedQuestion.Create('Trying to override already assigned optional');
aDst.FHasValue := aSrc.FHasValue;
if aSrc then
aDst.FValue := aSrc.FValue;
end;
Note: you also need to define EAlreadyAssignedQuestion as exception type:
EAlreadyAssignedQuestion = class(Exception);
But this is only a runtime check and is not enforced on compiletime