11 Stimmen

Verwendung generischer Container in Delphi XE - immer?

Generische Container können eine Zeitersparnis darstellen, wenn man ein Element hat und eine stark typisierte Liste dieser Elemente. Es spart die repetitive Codierung der Erstellung einer neuen Klasse mit möglicherweise einer internen TList-Variablen und typisierten Add/Delete-Typ-Methoden, neben anderen Vorteilen (wie all die neuen Funktionalitäten, die durch die generischen Container-Klassen bereitgestellt werden).

Ist es jedoch empfehlenswert, generische Container für stark typisierte Listen immer zu verwenden? Was sind die spezifischen Nachteile dabei? (Wenn man sich keine Sorgen um die Abwärtskompatibilität des Codes macht.) Ich habe gestern eine Serveranwendung geschrieben und hatte eine Liste von Elementen, die ich 'auf die alte Art und Weise' erstellt habe und die ich durch eine generische Liste ersetzen wollte, aber ich entschied mich, es schlank zu halten, größtenteils aus Gewohnheit. (Sollten wir die Gewohnheit brechen und eine neue beginnen, indem wir immer generische Container verwenden?)

12voto

David Heffernan Punkte 585606

In Delphi XE gibt es keinen Grund, generische Container nicht zu verwenden.

Der Wechsel von der alten Methode mit Casting bringt Ihnen:

  • s
  • enum
  • diebessere Leistungseigenschaften.

10voto

Cosmin Prund Punkte 25218

Dies wurde durch die Antwort von Deltic inspiriert, ich wollte ein Gegenbeispiel liefern, das beweist, dass Sie Generics für die Tierfütterungsroutine verwenden können. (dh: Polymorphe generische Liste)

Zunächst einige Hintergrundinformationen: Der Grund, warum Sie generische Tiere mit einer generischen Basisklassenliste füttern können, liegt darin, dass Sie normalerweise eine solche Vererbung haben:

TBaseList = Klasse
  // Einige Codezeilen, um dies tatsächlich zu einer Liste zu machen
end

TSpecificList = Klasse(TBaseList)
  // Code, der die Routinen Add und GetItem wieder einführt, um die TSpecificList in eine typsichere Liste eines anderen Typs zu verwandeln, die mit der TBaseList kompatibel ist
end

Dies funktioniert nicht mit Generics, weil Sie normalerweise dies haben:

TDogList = TList
end

TCatList = TList
end

... und der einzige "gemeinsame Vorfahr" für beide Listen ist TObject - überhaupt nicht hilfreich. Aber wir können einen neuen generischen Listentyp definieren, der zwei Klassenargumente annimmt: ein TAnimal und ein TSpecificAnimal, um eine typsichere Liste von TSpecificAnimal zu erstellen, die mit einer generischen Liste von TAnimal kompatibel ist. Hier ist die grundlegende Typdefinition:

TCompatibleList = Klasse(TObjectList)
privat
  function GetItem(i: Integer): T2;
public
  procedure Add(A:T2);
  Eigenschaft Item[i:Integer]:T2 lesen GetItem;Default;
end;

Unter Verwendung dessen können wir folgendes tun:

TAnimal = Klasse; 
TDog = Klasse(TAnimal); 
TCat = Klasse(TAnimal);

TDogList = TCompatibleList;
TCatList = TCompatibleList;

Auf diese Weise erben sowohl TDogList als auch TCatList tatsächlich von TObjectList, sodass wir jetzt eine polymorphe generische Liste haben!

Hier ist eine vollständige Konsolenanwendung, die dieses Konzept in Aktion zeigt. Und diese Klasse wird jetzt in meine ClassLibrary zur zukünftigen Wiederverwendung aufgenommen!

Programm Projekt23;

{$APPTYPE CONSOLE}

Verwendet
  SysUtils, Generics.Collections;

Art

  TAnimal = Klasse
  end;

  TDog = Klasse(TAnimal)
  end;

  TCat = Klasse(TAnimal)
  end;

  TCompatibleList = Klasse(TObjectList)
  privat
    function GetItem(i: Integer): T2;
  public
    procedure Add(A:T2);
    Eigenschaft Item[i:Integer]:T2 lesen GetItem;Default;
  end;

{ TX }

Verfahren TCompatibleList.Add(A: T2);
begin
  Geerbt Add(T1(TObject(A)));
end;

Funktion TCompatibleList.GetItem(i: Integer): T2;
begin
  Ergebnis := T2(TObject(Geerbt Items[i]));
end;

Verfahren FeedTheAnimals(L: TObjectList);
var A: TAnimal;
begin
  für A in L tun
    SchreibeLn('Füttern einer ' + A.ClassName);
end;

var Hunde: TCompatibleList;
    Katzen: TCompatibleList;
    Gemischt: TObjectList;

begin
  versuchen
    // Füttere einige Hunde
    Hunde := TCompatibleList.Erstellen;
    versuchen
      Hunde.Add(TDog.Erstellen);
      FeedTheAnimals(Hunde);
    endlich Hunde.Free;
    end;
    // Füttere einige Katzen
    Katzen := TCompatibleList.Erstellen;
    versuchen
      Katzen.Add(TCat.Erstellen);
      FeedTheAnimals(Katzen);
    endlich Katzen.Free;
    end;
    // Füttere eine gemischte Gruppe
    Gemischt := TObjectList.Erstellen;
    versuchen
      Gemischt.Add(TDog.Erstellen);
      Gemischt.Add(TCat.Erstellen);
      FeedTheAnimals(Gemischt);
    endlich Gemischt.Free;
    end;
    Readln;
  außer
    auf E: Ausnahme tun
      SchreibeLn(E.ClassName, ': ', E.Message);
  end;
end.

4voto

Robert Love Punkte 12109

Sollten wir die Gewohnheit brechen und eine neue beginnen, indem wir immer Generika verwenden? JA

3voto

Mason Wheeler Punkte 79858

In den meisten Fällen sind generische Container eine gute Sache.

Allerdings generiert der Compiler eine Menge doppelten Code, und leider weiß der Linker noch nicht, wie man ihn entfernt, sodass die häufige Verwendung von Generics zu einem aufgeblähten ausführbaren Programm führen könnte. Aber bis auf das sind sie großartig.

2voto

David Heffernan Punkte 585606

Im Geiste von Cosmins Antwort, im Grunde eine Antwort auf Deltics Antwort, hier ist wie man Deltics Code repariert:

type
  TAnimal = class
  end;

  TDog = class(TAnimal)
  end;

  TAnimalList = class(TList)
    procedure Feed;
  end;
  TDogList = TAnimalList;

Jetzt kannst du schreiben:

var
  Dogs: TDogList;
...
  Dogs.Feed;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X