11 Stimmen

Class Helper für generische Klasse?

Ich verwende Delphi 2009. Ist es möglich, einen Klassenhelfer für eine generische Klasse zu schreiben, z. B. für TQueue . Das Offensichtliche

TQueueHelper <T> = class helper of TQueue <T>
  ...
end;

funktioniert nicht, und auch nicht

TQueueHelper = class helper of TQueue
  ...
end;

13voto

Deltics Punkte 21121

Wie in der Delphi-Hilfe dokumentiert, sind Klassen-Helfer nicht für den allgemeinen Gebrauch gedacht, und sie werden fälschlicherweise als mit einer Reihe von Einschränkungen oder sogar Fehlern behaftet angesehen.

Nichtsdestotrotz herrscht die - meiner Meinung nach falsche und gefährliche - Auffassung vor, dass es sich dabei um ein legitimes Instrument aus dem allgemeinen "Werkzeugkasten" handelt. Ich habe gebloggt über warum dies falsch ist und anschließend darüber, wie Sie die Gefahren ein wenig abmildern können, indem Sie nach einem sozial verantwortlichen Kodierungsmuster (obwohl auch das nicht hundertprozentig sicher ist).

Sie können einen Großteil der Wirkung eines Klassenhelfers erreichen, ohne jede dieser Fehler oder Einschränkungen oder (vor allem) Risiken indem Sie einen harten Cast auf eine "Pseudo"-Klasse verwenden, die von der Klasse abgeleitet ist, die Sie zu erweitern versuchen, d.h. statt:

TFooHelper = class helper for TFoo
  procedure MyHelperMethod;
end;

utilice

TFooHelper = class(TFoo)
  procedure MyHelperMethod;
end;

Genau wie bei einem "formalen" Helfer, instanziieren Sie niemals diese TFooHelper Klasse, verwenden Sie sie ausschließlich zum Mutieren der TFoo Klasse, aber in diesem Fall müssen Sie explizit sein. Wenn Sie in Ihrem Code eine Instanz einer der folgenden Klassen verwenden müssen TFoo Wenn Sie Ihre "Helfer"-Methoden anwenden, müssen Sie einen harten Wurf machen:

   TFooHelper(someFoo).MyHelperMethod;

Die Nachteile:

  1. müssen Sie sich an dieselben Regeln halten, die auch für Helfer gelten - keine Mitgliedsdaten usw. (das ist eigentlich kein Nachteil, außer dass der Compiler Sie nicht daran "erinnert").

  2. Sie müssen explizit einen Wurf ausführen, um Ihren Helfer zu verwenden

  3. Wenn Sie einen Helfer verwenden, um geschützte Mitglieder offenzulegen, müssen Sie den Helfer in der gleichen Einheit deklarieren, in der Sie ihn verwenden (es sei denn, Sie legen eine öffentliche Methode offen, die die erforderlichen geschützten Mitglieder offenlegt)

Vorteile:

  1. Es besteht absolut KEIN Risiko, dass Ihre Hilfsklasse kaputt geht, wenn Sie einen anderen Code verwenden, der derselben Basisklasse "hilft".

  2. Die explizite Typisierung macht in Ihrem "Verbraucher"-Code deutlich, dass Sie mit der Klasse auf eine Weise arbeiten, die von der Klasse selbst nicht direkt unterstützt wird, anstatt diese Tatsache hinter einem syntaktischen Zucker zu verstecken.

Es ist nicht so "sauber" wie ein Klassenhelfer, aber in diesem Fall bedeutet der "saubere" Ansatz eigentlich nur, das Chaos unter den Teppich zu kehren, und wenn jemand den Teppich stört, hat man am Ende ein noch größeres Chaos als am Anfang.

13voto

Kenneth Cochran Punkte 11762

Ich verwende derzeit noch Delphi 2009, also dachte ich, ich würde ein paar andere Möglichkeiten hinzufügen, um eine generische Klasse zu erweitern. Diese sollten in neueren Versionen von Delphi genauso gut funktionieren. Schauen wir uns an, wie es aussehen würde, eine ToArray Methode zu einer List-Klasse.

Abfangjäger Klassen

Interceptor-Klassen sind Klassen, die den gleichen Namen wie die Klasse tragen, von der sie erben:

TList<T> = class(Generics.Collections.TList<T>)
public
  type
    TDynArray = array of T;
  function ToArray: TDynArray;
end;

function TList<T>.ToArray: TDynArray;
var
  I: Integer;
begin
  SetLength(Result, self.Count);
  for I := 0 to Self.Count - 1 do
  begin
    Result[I] := Self[I];
  end;
end;

Beachten Sie, dass Sie den vollständig qualifizierten Namen verwenden müssen, Generics.Collections.TList<T> als Vorfahre. Andernfalls erhalten Sie E2086 Type '%s' is not completely defined .

Der Vorteil dieser Technik besteht darin, dass Ihre Erweiterungen weitgehend transparent sind. Sie können Instanzen der neuen TList überall dort verwenden, wo das Original verwendet wurde.

Diese Technik hat zwei Nachteile:

  • Es kann bei anderen Entwicklern Verwirrung stiften, wenn sie nicht wissen, dass Sie eine bekannte Klasse neu definiert haben.
  • Sie kann nicht für eine versiegelte Klasse verwendet werden.

Die Verwirrung kann durch eine sorgfältige Benennung der Einheiten und die Vermeidung der Verwendung der "Original"-Klasse an derselben Stelle wie Ihre Abfangklasse gemildert werden. Versiegelte Klassen sind in den von Embarcadero gelieferten rtl/vcl-Klassen kein großes Problem. Ich habe im gesamten Quellbaum nur zwei versiegelte Klassen gefunden: TGCHandleList (wird nur im nicht mehr existierenden Delphi.NET verwendet) und TCharacter. Es kann jedoch zu Problemen mit Bibliotheken von Drittanbietern kommen.

Le site Dekorateur Muster

Mit dem Dekoratormuster können Sie eine Klasse dynamisch erweitern, indem Sie sie mit einer anderen Klasse umhüllen, die ihre öffentliche Schnittstelle erbt:

TArrayDecorator<T> = class abstract(TList<T>)
public
  type
    TDynArray = array of T;
  function ToArray: TDynArray; virtual; abstract;
end;

TArrayList<T> = class(TArrayDecorator<T>)
private
  FList: TList<T>;
public
  constructor Create(List: TList<T>);
  function ToArray: TListDecorator<T>.TDynArray; override;
end;

function TMyList<T>.ToArray: TListDecorator<T>.TDynArray;
var
  I: Integer;
begin
  SetLength(Result, self.Count);
  for I := 0 to Self.Count - 1 do
  begin
    Result[I] := FList[I];
  end;
end;

Auch hier gibt es Vor- und Nachteile.

Vorteile

  • Sie können die Einführung der neuen Funktion aufschieben, bis sie tatsächlich benötigt wird. Sie müssen eine Liste in ein Array umwandeln? Konstruieren Sie eine neue TArrayList und übergeben Sie eine TList oder einen Abkömmling als Parameter im Konstruktor. Wenn Sie fertig sind, verwerfen Sie einfach die TArrayList.
  • Sie können zusätzliche Dekoratoren erstellen, die weitere Funktionen hinzufügen, und Dekoratoren auf verschiedene Weise kombinieren. Sie können damit sogar Mehrfachvererbung simulieren, obwohl Schnittstellen immer noch einfacher sind.

Benachteiligungen

  • Es ist ein wenig komplexer zu verstehen.
  • Das Anwenden von mehreren Dekoratoren auf ein Objekt kann zu langatmigen Konstruktorketten führen.
  • Wie bei Abfangjägern können Sie eine versiegelte Klasse nicht erweitern.

Nebenbemerkung

Es scheint also, dass, wenn Sie eine Klasse fast unmöglich zu erweitern machen wollen, machen es eine versiegelte generische Klasse. Dann können Klassenhelfer sie nicht berühren und es kann nicht von ihr geerbt werden. Die einzige Möglichkeit, die bleibt, ist das Wrapping.

8voto

Mason Wheeler Punkte 79858

Soweit ich das beurteilen kann, gibt es keine Möglichkeit, einen Klassenhelfer auf eine generische Klasse zu setzen und sie zu kompilieren. Sie sollten das der QC als Fehler melden.

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