Delphi: tipi diversi dall'intero per indicizzare gli elementi TstringList
-
28-10-2019 - |
Domanda
Arrays
Può essere indicizzato utilizzando tipi enumerati definiti dall'utente. Per esempio:
type
TIndexValue = (ZERO = 0, ONE, TWO, THREE, FOUR);
var
MyArray: array[Low(TIndexValue) .. High(TIndexValue)] of String;
Gli elementi di questo array possono quindi essere referenziati utilizzando TIndexValue
valori come indice:
MyArray[ZERO] := 'abc';
Sto cercando di ottenere questo stesso generale funzionalità con a TStringList
.
Una soluzione semplice è lanciare ogni valore dell'indice a un Integer
Digita al momento del riferimento:
MyStringList[Integer(ZERO)] := 'abc';
Un'altra soluzione (per nascondere tutto il casting) è creare una sottoclasse di TStringList
e differire tutto il casting alle subroutine di questa sottoclasse che accedono alle eredità Strings
proprietà:
type
TIndexValue = (ZERO = 0, ONE, TWO, THREE, FOUR);
type
TEIStringList = class(TStringList)
private
function GetString(ItemIndex: TIndexValue): String;
procedure SetString(ItemIndex: TIndexValue; ItemValue: String);
public
property Strings[ItemIndex: TIndexValue]: String
read GetString write SetString; default;
end;
function TEIStringList.GetString(ItemIndex: TIndexValue): String;
begin
Result := inherited Strings[Integer(ItemIndex)];
end;
procedure TEIStringList.SetString(ItemIndex: TIndexValue; ItemValue: String);
begin
inherited Strings[Integer(ItemIndex)] := ItemValue;
end;
Funziona bene per un'unica implementazione che utilizza il tipo enumerato TIndexValue
.
Tuttavia, vorrei riutilizzare questa stessa logica o sottoclasse per diversi diversi TStringList
oggetti indicizzati da diversi tipi elencati, senza dover definire TStringList
sottoclassi per ciascun possibile tipo enumerato.
È possibile qualcosa di simile? Sospetto che potrei dover dipendere dai generici di Delphi, ma sarei molto interessato ad apprendere che ci sono modi più semplici per raggiungere questo obiettivo.
Soluzione
Penso che i generici sarebbero di gran lunga la soluzione più elegante. Usarli sarebbe semplice come riscrivere la tua classe sopra come:
TEIStringList<T> = class(TStringList)
e quindi sostituendo tutti i riferimenti di Tindexvalue con T. Quindi potresti crearlo come qualsiasi altro generico:
var
SL: TEIStringList<TIndexValue>;
begin
SL:=TEIStringList<TIndexValue>.Create;
(...)
ShowMessage(SL[ZERO])
(...)
end;
Se insisti per evitare i generici, forse il sovraccarico dell'operatore sarebbe utile. Qualcosa come il seguente dovrebbe funzionare:
type
TIndexValueHolder = record
Value : TIndexValue;
class operator Implicit(A: TMyRecord): integer;
end;
(...)
class operator TIndexValueHolder.Implicit(A: TMyRecord): integer;
begin
Result:=Integer(A);
end;
Quindi usa con:
var
Inx : TIndexValueHolder;
begin
Inx.Value:=ZERO;
ShowMessage(SL[Inx]);
end
AGGIORNAMENTO: è possibile adattare i metodi TindexValueholder per l'uso in un loop aggiungendo i metodi successivi, Hasnext, ecc.. Questo potrebbe finire per sconfiggere lo scopo, però. Non sono ancora sicuro di quale sia lo scopo, o perché questo sarebbe utile, ma ecco alcune idee su come farlo, comunque.
Altri suggerimenti
Probabilmente è possibile utilizzare un aiutante di classe e dichiarare l'indice di proprietà predefinito come variante:
type
TEnum1 = (Zero = 0, One, Two, Three, Four);
TEnum2 = (Nul = 0, Een, Twee, Drie, Vier);
TEnum3 = (Gds = 0, Psajs, Oeroifd, Vsops, Wowid);
TStringListHelper = class helper for TStringList
private
function GetString(Index: Variant): String;
procedure SetString(Index: Variant; const Value: String);
public
property Strings[Index: Variant]: String read GetString write SetString;
default;
end;
function TStringListHelper.GetString(Index: Variant): String;
begin
Result := inherited Strings[Index];
end;
procedure TStringListHelper.SetString(Index: Variant; const Value: String);
begin
inherited Strings[Index] := Value;
end;
Codice di test:
procedure TForm1.Button1Click(Sender: TObject);
var
Strings: TStringList;
begin
Strings := TStringList.Create;
try
Strings.Add('Line 1');
Strings.Add('Second line');
Strings[Zero] := 'First line';
Memo1.Lines.Assign(Strings);
Caption := Strings[Psajs];
finally
Strings.Free;
end;
end;
Vedi la cronologia di modifica per un precedente tentativo meno riuscito.