Delphi 2009 を使用しています。ジェネリック クラス、つまり TQueue のクラス ヘルパーを作成することは可能ですか。明らかなこと
TQueueHelper <T> = class helper of TQueue <T>
...
end;
機能しない、機能しない
TQueueHelper = class helper of TQueue
...
end;
Delphi 2009 を使用しています。ジェネリック クラス、つまり TQueue のクラス ヘルパーを作成することは可能ですか。明らかなこと
TQueueHelper <T> = class helper of TQueue <T>
...
end;
機能しない、機能しない
TQueueHelper = class helper of TQueue
...
end;
私は現在も Delphi 2009 を使用しているので、ジェネリック クラスを拡張する方法をいくつか追加しようと考えました。これらは、新しいバージョンの Delphi でも同様に機能するはずです。ToArray
List クラスにメソッドを追加するとどうなるか見てみましょう。
インターセプター クラスは、継承元のクラスと同じ名前が付けられたクラスです。
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;
繰り返しになりますが、メリットとデメリットがあります。
利点
短所
サイドノート
したがって、クラスを拡張することをほぼ不可能にしたい場合は、それをシールされたジェネリック クラスにするようです。クラスヘルパーはそれに触れることができず、継承することもできません。残っている唯一のオプションについては、それをラップすることです。
Delphi ヘルプに記載されているように、クラス ヘルパーは汎用目的で使用するように設計されておらず、結果として多くの制限やバグがあると誤って認識されています。
それにもかかわらず、これらが汎用の「ツールキット」の正当なツールであるという認識があります-私の見解では正しくなく危険です. これが間違っている理由と、その後、社会的に責任のあるコーディング パターンに従うことで危険を軽減する方法についてブログに書いています(ただし、これは完全な証拠ではありません)。
拡張しようとしているクラスから派生した「疑似」クラスへのハード キャストを使用することで、これらのバグや制限、または (最も重要な)リスクなしで、クラス ヘルパーの効果を最大限に引き出すことができます。つまり、次の代わりに:
TFooHelper = class helper for TFoo
procedure MyHelperMethod;
end;
使用する
TFooHelper = class(TFoo)
procedure MyHelperMethod;
end;
「正式な」ヘルパーの場合と同様に、このTFooHelperクラスをインスタンス化することはありません。TFoo クラスを変更するためだけに使用しますが、この場合は明示的でなければなりません。コードで、「ヘルパー」メソッドを使用してTFooのインスタンスを使用する必要がある場合は、ハードキャストする必要があります。
TFooHelper(someFoo).MyHelperMethod;
欠点:
ヘルパーに適用されるのと同じルールに固執する必要があります-メンバーデータなどはありません(コンパイラーが「通知」しないことを除いて、実際にはまったく欠点ではありません)。
ヘルパーを使用するには、明示的にキャストする必要があります
ヘルパーを使用して保護されたメンバーを公開する場合は、それを使用するのと同じユニットでヘルパーを宣言する必要があります (必要な保護されたメンバーを公開するパブリック メソッドを公開しない限り)。
利点:
同じ基本クラスを「助ける」他のコードを使い始めた場合、ヘルパーが壊れるリスクは絶対にありません
明示的な型キャストにより、「消費者」コードで、クラス自体によって直接サポートされていない方法でクラスを操作していることを明確にします。
クラスヘルパーほど「きれい」ではありませんが、この場合の「よりきれいな」アプローチは、実際には敷物の下の混乱を一掃するだけであり、誰かが敷物を乱すと、最初よりも大きな混乱になります.
私が知る限り、ジェネリック クラスにクラス ヘルパーを配置してコンパイルする方法はありません。これはバグとして QC に報告する必要があります。