11

Delphi 2009 を使用しています。ジェネリック クラス、つまり TQueue のクラス ヘルパーを作成することは可能ですか。明らかなこと

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

機能しない、機能しない

TQueueHelper = class helper of TQueue
  ...
end;
4

3 に答える 3

13

私は現在も Delphi 2009 を使用しているので、ジェネリック クラスを拡張する方法をいくつか追加しようと考えました。これらは、新しいバージョンの Delphi でも同様に機能するはずです。ToArrayList クラスにメソッドを追加するとどうなるか見てみましょう。

インターセプタークラス

インターセプター クラスは、継承元のクラスと同じ名前が付けられたクラスです。

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;

Generics.Collections.TList<T>祖先として完全修飾名を使用する必要があることに注意してください。そうでなければ、あなたは得るでしょうE2086 Type '%s' is not completely defined

この手法の利点は、拡張機能がほとんど透過的であることです。元の TList が使用されていた場所ならどこでも、新しい TList のインスタンスを使用できます。

この手法には 2 つの欠点があります。

  • 使い慣れたクラスを再定義したことを他の開発者が認識していないと、混乱を招く可能性があります。
  • シールド クラスでは使用できません。

この混乱は、慎重にユニットに名前を付け、インターセプター クラスと同じ場所で「元の」クラスを使用しないようにすることで軽減できます。封印されたクラスは、Embarcadero が提供する rtl/vcl クラスではあまり問題になりません。ソース ツリー全体で 2 つの封印されたクラスしか見つかりませんでした: TGCHandleList (現在は廃止された Delphi.NET でのみ使用されます) と TCharacter です。ただし、サードパーティのライブラリで問題が発生する場合があります。

デコレータパターン_

デコレーター パターンを使用すると、パブリック インターフェイスを継承する別のクラスでクラスをラップすることにより、クラスを動的に拡張できます。

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;

繰り返しになりますが、メリットとデメリットがあります。

利点

  • 実際に必要になるまで、新しい機能の導入を延期できます。リストを配列にダンプする必要がありますか? 任意の TList または子孫をコンストラクターのパラメーターとして渡して、新しい TArrayList を構築します。完了したら、TArrayList を破棄します。
  • より多くの機能を追加する追加のデコレータを作成し、さまざまな方法でデコレータを組み合わせることができます。インターフェイスの方が簡単ですが、多重継承をシミュレートするためにも使用できます。

短所

  • 理解するのはもう少し複雑です。
  • オブジェクトに複数のデコレータを適用すると、コンストラクタ チェーンが冗長になる可能性があります。
  • インターセプターと同様に、sealed クラスを拡張することはできません。

サイドノート

したがって、クラスを拡張することをほぼ不可能にしたい場合は、それをシールされたジェネリック クラスにするようです。クラスヘルパーはそれに触れることができず、継承することもできません。残っている唯一のオプションについては、それをラップすることです。

于 2011-09-15T20:55:07.000 に答える
13

Delphi ヘルプに記載されているように、クラス ヘルパーは汎用目的で使用するように設計されておらず、結果として多くの制限やバグがあると誤って認識されています。

それにもかかわらず、これらが汎用の「ツールキット」の正当なツールであるという認識があります-私の見解では正しくなく危険です. これが間違っている理由と、その後、社会的に責任のあるコーディング パターンに従うことで危険を軽減する方法についてブログに書いています(ただし、これは完全な証拠ではありません)。

拡張しようとしているクラスから派生した「疑似」クラスへのハード キャストを使用することで、これらのバグや制限、または (最も重要な)リスクなしで、クラス ヘルパーの効果を最大限に引き出すことができます。つまり、次の代わりに:

TFooHelper = class helper for TFoo
  procedure MyHelperMethod;
end;

使用する

TFooHelper = class(TFoo)
  procedure MyHelperMethod;
end;

「正式な」ヘルパーの場合と同様に、このTFooHelperクラスをインスタンス化することはありません。TFoo クラスを変更するためだけに使用しますが、この場合は明示的でなければなりません。コードで、「ヘルパー」メソッドを使用してTFooのインスタンスを使用する必要がある場合は、ハードキャストする必要があります。

   TFooHelper(someFoo).MyHelperMethod;

欠点:

  1. ヘルパーに適用されるのと同じルールに固執する必要があります-メンバーデータなどはありません(コンパイラーが「通知」しないことを除いて、実際にはまったく欠点ではありません)。

  2. ヘルパーを使用するには、明示的にキャストする必要があります

  3. ヘルパーを使用して保護されたメンバーを公開する場合は、それを使用するのと同じユニットでヘルパーを宣言する必要があります (必要な保護されたメンバーを公開するパブリック メソッドを公開しない限り)。

利点:

  1. 同じ基本クラスを「助ける」他のコードを使い始めた場合、ヘルパーが壊れるリスクは絶対にありません

  2. 明示的な型キャストにより、「消費者」コードで、クラス自体によって直接サポートされていない方法でクラスを操作していることを明確にします。

クラスヘルパーほど「きれい」ではありませんが、この場合の「よりきれいな」アプローチは、実際には敷物の下の混乱を一掃するだけであり、誰かが敷物を乱すと、最初よりも大きな混乱になります.

于 2009-10-21T18:33:55.380 に答える
8

私が知る限り、ジェネリック クラスにクラス ヘルパーを配置してコンパイルする方法はありません。これはバグとして QC に報告する必要があります。

于 2009-10-21T11:35:06.150 に答える