uses
TypInfo, Variants;
type
TNonNullable<T> =
record
class operator Implicit(
const Value: TNonNullable<T>): T;
inline;
class operator Implicit(
const Value: T): TNonNullable<T>;
inline;
class procedure Check(
const Value: T);
static;
public type
ENonNullable =
class(
Exception);
private
FValue: T;
end;
{ TNonNullable<T> }
class procedure TNonNullable<T>.Check(
const Value: T);
begin
case PTypeInfo(TypeInfo(T)).Kind
of
tkPointer, tkClass, tkClassRef, tkInterface, tkProcedure, tkString, tkUString, tkDynArray, tkWString:
if PPointer(@Value)^ <>
nil then
Exit;
tkInteger, tkChar, tkWChar, tkEnumeration, tkSet:
case GetTypeData(TypeInfo(T)).OrdType
of
otSByte, otUByte:
if PByte(@Value)^ <> 0
then
Exit;
otSWord, otUWord:
if PWord(@Value)^ <> 0
then
Exit;
otSLong, otULong:
if PLongWord(@Value)^ <> 0
then
Exit;
else
raise ENonNullable.CreateFmt('
TNonNullable<T>: Type %s is not supported.', [GetTypeName(TypeInfo(T))]);
end;
tkInt64:
if PInt64(@Value)^ <> 0
then
Exit;
tkFloat:
case GetTypeData(TypeInfo(T)).FloatType
of
ftSingle:
if PSingle(@Value)^ <> 0
then
Exit;
ftDouble:
if PDouble(@Value)^ <> 0
then
Exit;
ftExtended:
if PExtended(@Value)^ <> 0
then
Exit;
ftCurr:
if PCurrency(@Value)^ <> 0
then
Exit;
else
raise ENonNullable.CreateFmt('
TNonNullable<T>: Type %s is not supported.', [GetTypeName(TypeInfo(T))]);
end;
tkMethod:
if TMethod(Pointer(@Value)^).Code <>
nil then
Exit;
tkVariant:
if not VarIsNull(PVariant(@Value)^)
and VarIsEmpty(PVariant(@Value)^)
then
Exit;
else
raise ENonNullable.CreateFmt('
TNonNullable<T>: Type %s is not supported.', [GetTypeName(TypeInfo(T))]);
end;
raise ENonNullable.CreateFmt('
TNonNullable<T>: Variable %s(%p) is null.', [GetTypeName(TypeInfo(T)), @Value]);
end;
class operator TNonNullable<T>.Implicit(
const Value: TNonNullable<T>): T;
begin
Check(Value.FValue);
Result := Value.FValue;
end;
class operator TNonNullable<T>.Implicit(
const Value: T): TNonNullable<T>;
begin
Check(Value);
Result.FValue := Value;
end;