В Delphi вроде как нет Nullable типов, но если нет, но очень хочется, то они могут появиться ))) Читал книгу Delphi Memory Management, и там наткнулся на раздел про Nullable, автор книги ссылался на MVP Allen Bauer.
Идея проста…
To implement a nullable type, we need to create a wrapper around a value type and add an
undefined flag. That part can be easily achieved with a generic record storing our wrapped
Value and HasValue flag.
А вот и первоисточник…
Класс Nullable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
unit uNullable; interface uses System.SysUtils, System.Classes, System.Generics.Defaults; type TNullable<T> = record strict private FValue: T; FHasValue: IInterface; function GetValue: T; function GetHasValue: boolean; public constructor Create(AValue: T); function ValueOrDefault: T; overload; function ValueOrDefault(Default: T): T; overload; property HasValue: boolean read GetHasValue; property Value: T read GetValue; class operator Implicit(Value: TNullable<T>): T; class operator Implicit(Value: T): TNullable<T>; class operator Explicit(Value: TNullable<T>): T; class operator NotEqual(const Left, Right: TNullable<T>): boolean; class operator Equal(const Left, Right: TNullable<T>): boolean; end; procedure SetFakeInterface(var Intf: IInterface); implementation function NopAddRef(inst: Pointer): integer; stdcall; begin Result := -1; end; function NopRelease(inst: Pointer): integer; stdcall; begin Result := -1; end; function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj): HResult; stdcall; begin Result := E_NOINTERFACE; end; const FakeInterfaceVTable: array[0..2] of Pointer = (@NopQueryInterface, @NopAddRef, @NopRelease); FakeInterfaceInstance: Pointer = @FakeInterfaceVTable; procedure SetFakeInterface(var Intf: IInterface); begin Intf := IInterface(@FakeInterfaceInstance); end; constructor TNullable<T>.Create(AValue: T); begin FValue := AValue; SetFakeInterface(FHasValue); end; function TNullable<T>.GetHasValue: boolean; begin Result := FHasValue <> nil; end; function TNullable<T>.GetValue: T; begin if not HasValue then raise Exception.Create('Invalid operation, Nullable type has no value'); Result := FValue; end; function TNullable<T>.ValueOrDefault: T; begin if HasValue then Result := fValue else Result := default(T); end; function TNullable<T>.ValueOrDefault(Default: T): T; begin if not HasValue then Result := Default else Result := fValue; end; class operator TNullable<T>.Explicit(Value: TNullable<T>): T; begin Result := Value.Value; end; class operator TNullable<T>.Implicit(Value: TNullable<T>): T; begin Result := Value.Value; end; class operator TNullable<T>.Implicit(Value: T): TNullable<T>; begin Result := TNullable<T>.Create(Value); end; class operator TNullable<T>.Equal(const Left, Right: TNullable<T>): boolean; var Comparer: IEqualityComparer<T>; begin if Left.HasValue and Right.HasValue then begin Comparer := TEqualityComparer<T>.Default; Result := Comparer.Equals(Left.Value, Right.Value); end else Result := Left.HasValue = Right.HasValue; end; class operator TNullable<T>.NotEqual(const Left, Right: TNullable<T>): boolean; var Comparer: IEqualityComparer<T>; begin if Left.HasValue and Right.HasValue then begin Comparer := TEqualityComparer<T>.Default; Result := not Comparer.Equals(Left.Value, Right.Value); end else Result := Left.HasValue <> Right.HasValue; end; end. |
Пример использования
1 2 3 4 5 6 7 8 9 10 11 |
var n: ISmartPointer<TNullableClassExample>; begin ReportMemoryLeaksOnShutdown := true; n := TSmartPointer<TNullableClassExample>.Create(); // n.SomeNullableValue:='testString'; // if uncomment will not be nullable if n.SomeNullableValue.HasValue then ShowMessage('has value') else ShowMessage('no value'); end; |