IV. Conception d'un record générique▲
Histoire d'aller voir d'un autre côté, développons le record simple, mais utile TNullable<T>. Son but est de valoir soit une valeur de type T, soit nil. Il est fort probable que vous ayez déjà eu besoin d'un tel type, par exemple pour représenter la valeur NULL des bases de données.
Ce record contiendra deux champs : FValue de type T et FIsNil de type Boolean, ainsi que deux propriétés permettant de lire (mais pas d'écrire) ces champs. On se servira uniquement d'opérateurs de conversion implicite pour construire des valeurs de ce type.
unit
Generics.Nullable;
interface
type
TNullable<T> = record
private
FValue: T;
FIsNil: Boolean
;
public
class
operator Implicit(const
Value: T): TNullable<T>;
class
operator Implicit(Value: Pointer
): TNullable<T>;
class
operator Implicit(const
Value: TNullable<T>): T;
property
IsNil: Boolean
read
FIsNil;
property
Value: T read
FValue;
end
;
Pour plus d'infos sur les redéfinitions d'opérateurs, consultez le tutoriel La surcharge d'opérateurs sous Delphi 2006 Win32La surcharge d'opérateurs sous Delphi 2006 Win32 par Laurent Dardenne de Laurent Dardenne.
C'est donc un type immuable (dont on ne peut plus modifier l'état une fois qu'il est créé).
L'implémentation des trois opérateurs de conversion est assez simple. Le deuxième d'entre eux (celui avec un paramètre de type Pointer) est là pour permettre l'affectation := nil.
uses
SysUtils;
resourcestring
sCantConvertNil = 'Ne peut convertir nil'
;
sOnlyValidValueIsNil = 'La seule valeur valide est nil'
;
class
operator TNullable<T>.Implicit(const
Value: T): TNullable<T>;
begin
Result.FValue := Value;
Result.FIsNil := False
;
end
;
class
operator TNullable<T>.Implicit(Value: Pointer
): TNullable<T>;
begin
Assert(Value = nil
, sOnlyValidValueIsNil);
Result.FIsNil := True
;
end
;
class
operator TNullable<T>.Implicit(const
Value: TNullable<T>): T;
begin
if
Value.IsNil then
raise
EConvertError.Create(sCantConvertNil);
Result := Value.FValue;
end
;
On peut l'utiliser très simplement comme ceci :
var
Value: Integer
;
NullValue: TNullable<Integer
>;
begin
NullValue := 5
;
WriteLn(Integer
(NullValue));
NullValue := nil
;
if
NullValue.IsNil then
WriteLn('nil'
)
else
WriteLn(NullValue.Value);
NullValue := 10
;
Value := NullValue;
WriteLn(Value);
end
;
Ce qui affiche bien :
5
nil
10
Vous avez tout compris ? C'est génial, parce que je n'ai donné aucune explication. C'est bien la preuve que c'est simple comme bonjour, les génériques :-).
Le code source complet de Generics.Nullable se trouve dans le zip des sources de ce tutoriel, téléchargeable en fin de tutorielTélécharger les sources.