2010-07-31 6 views
0

Dans mon application, j'ai créé la liste de type TList, destiné à stocker ou Entiers Doubles:Comment changer une valeur de type générique?

TKList<T> = class 
    private 
    FItems: TList<T>; 
    function GetItem(Index: Integer): T; 
    procedure SetItem(Index: Integer; const Value: T); 
    function GetMaxValue(): T; 
    function GetMinValue(): T; 
    public 
    constructor Create; overload; 
    constructor Create(const AKList: TKList<T>); overload; 
    destructor Destroy; override; 
    procedure Assign(const AKList: TKList<T>); 
    function Add(const AValue: T): Integer; 
    procedure Clear; 
    function Count: Integer; 
    procedure Invert; 
    function ToString: string; override; 
    function Info: string; 
    property Values[Index: Integer]: T read GetItem write SetItem; default; 
    end; 

Comment puis-je mettre en œuvre la procédure inverti() pour inverser les valeurs dans la liste générique?

Merci d'avance.

+0

Qu'est-ce que cela signifie pour « inverser » les valeurs? S'il vous plaît être plus précis sur les problèmes que vous rencontrez la mise en œuvre de cette méthode. –

+0

Vous voulez dire inverser l'ordre des articles? Vous pouvez le faire en créant une deuxième liste, en revenant en arrière sur la liste d'origine à la fin et en ajoutant les éléments à la nouvelle liste, puis en remplaçant l'original par la nouvelle liste. –

+1

Il a parlé de nombres entiers et de doubles, donc je suppose: pour I: = 0 à compter - 1 faire Valeurs [I]: = -Valeurs [I]; –

Répondre

2

Il n'existe aucun moyen de spécifier des contraintes sur les génériques de sorte que vous pouvez exiger que les types soient des nombres, donc vous ne pouvez pas utiliser les opérateurs numériques sur les valeurs de votre liste. Craig Stuntz a écrit a series of posts décrivant comment construire une bibliothèque statistique générique, et il a rencontré le même problème. Il l'a résolu en fournissant des arguments supplémentaires à ses fonctions afin que l'appelant puisse fournir des implémentations pour les opérations numériques spécifiques au type - le modèle modèle de méthode. Voilà comment il a déclaré l'opération Average:

type 
    TBinaryOp<T> = reference to function(ALeft, ARight: T): T 

    TStatistics<T> = class 
    public 
     class function Average(const AData: TEnumerable<T>; 
          AAdder, ADivider: TBinaryOp<T>; 
          AMapper: TFunc<integer, T>): T; overload; 

Les appelants de cette fonction doivent fournir leur propre code pour ajouter, en divisant et « cartographie » le type générique. (Cartographie est couvert dans un poste plus tard et n'a pas d'importance ici.) Vous pouvez écrire votre fonction Invert comme ceci:

type 
    TUnaryOp<T> = reference to function(Arg: T): T; 

    TKList<T> = class 
    procedure Invert(ANegater: TUnaryOp<T>); 

procedure TKList<T>.Invert; 
var 
    i: Integer; 
begin 
    for i := 0 to Pred(Count) do 
    Values[i] := ANegater(Values[i]); 
end; 

Pour le rendre plus commode d'appeler les méthodes sans avoir à fournir des arguments supplémentaires tous les Stuntz a montré comment déclarer un descendant spécifique au type qui fournit les bons arguments. Vous pouvez le faire comme ceci:

type 
    TIntKList = class(TKList<Integer>) 
    private 
    class function Negate(Arg: Integer): Integer; 
    public 
    procedure Invert; 
    end; 

procedure TIntKList.Invert; 
begin 
    inherited Invert(Negate); 
end; 

Vous pouvez fournir des types de type spécifique descendants pour les numériques communs, et si les consommateurs de classe ont besoin d'utiliser d'autres types de numéros semblables, ils peuvent fournir leurs propres implémentations pour la opérations numériques de base sans avoir à ré-implémenter votre classe de liste entière.

5

En supposant que vous voulez dire inverser le tableau comme vous avez des valeurs 1, 3, 5 après avoir appelé cette fonction vous voulez avoir 5, 3, 1

Ensuite, vous pourriez mettre en œuvre la procédure comme celui-ci.

procedure TKList<T>.Invert; 
var 
    I: Integer; 
begin 
    for I := 0 to (Count - 1) div 2 do 
    FItems.Exchange(I, Count - I - 1); 
end; 

Altho Je suggère Reverse comme son nom, car Invert est assez confus.

+0

Inverser signifie changer le signe de chaque valeur. Donc 1, -3, 5 convertit en -1, 3 et -5. – Keeper

1

Merci Rob, je l'ai compris.

Quels sont les avantages/inconvénients a l'approche suivante:

procedure TKList<T>.Invert; 
var 
    i: Integer; 
    Val: TValue; 
begin 

    if TTypeInfo(TypeInfo(T)^).Kind = tkInteger then 
    begin 
    for i := 0 to FItems.Count - 1 do 
    begin 
     Val := TValue.From<T>(FItems[i]); 
     TValue.From<Integer>(-Val.AsInteger).AsType<T>; 
    end; 
    end 
    else if TTypeInfo(TypeInfo(T)^).Kind = tkFloat then 
    begin 
    for i := 0 to FItems.Count - 1 do 
    begin 
     Val := TValue.From<T>(FItems[i]); 
     FItems[i] := TValue.From<Double>(-Val.AsExtended).AsType<T>; 
    end; 
    end; 

end; 
+0

Si je crée un autre type de numéro, je ne peux pas le stocker dans votre liste censée traiter des nombres. En outre, vous devez décider si vous utilisez Extended ou Double. Et si la classe "générique" ne peut gérer que deux types, je me demande s'il vaut la peine d'être générique du tout. –

Questions connexes