質問
Delphi(およびおそらく他の多くの言語)にはクラスヘルパーがあります。これらは、既存のクラスに追加のメソッドを追加する方法を提供します。サブクラスを作成しません。
では、クラスヘルパーの良い用途は何ですか?
解決
私はそれらを使用しています:
- 列挙子を実装していないVCLクラスに挿入します。
- 拡張 VCLクラス。
-
私の派生リストおよびTStringList。
TGpStringListHelper = class helper for TStringList public function Last: string; function Contains(const s: string): boolean; function FetchObject(const s: string): TObject; procedure Sort; procedure Remove(const s: string); end; { TGpStringListHelper }
-
レコードフィールドおよびキャストを削除。
他のヒント
最初は、クラスヘルパーに懐疑的でした。しかし、その後、興味深いブログエントリそして今、私はそれらが本当に役立つと確信しています。
たとえば、既存のインスタンスクラスに追加の機能が必要な場合、何らかの理由で既存のソースを変更することはできません。クラスヘルパーを作成して、この機能を追加できます。
例:
type
TStringsHelper = class helper for TStrings
public
function IsEmpty: Boolean;
end;
function TStringsHelper.IsEmpty: Boolean;
begin
Result := Count = 0;
end;
常に、TStrings(のサブクラス)のインスタンスを使用し、TStringsHelperはスコープ内にあります。メソッドIsEmptyにアクセスできます。
例:
procedure TForm1.Button1Click(Sender: TObject);
begin
if Memo1.Lines.IsEmpty then
Button1.Caption := 'Empty'
else
Button1.Caption := 'Filled';
end;
注:
- クラスヘルパーは別のユニットに格納できるため、独自の気の利いたクラスヘルパーを追加できます。これらのユニットには、ClassesユニットのヘルパーにClassesHelpersなどの覚えやすい名前を付けてください。
- レコードヘルパーもあります。
- スコープ内に複数のクラスヘルパーが存在する場合、いくつかの問題を想定して、使用できるヘルパーは1つだけです。
これは、C#3(およびVB9)の拡張メソッドに非常によく似ています。私が彼らのために見た中で最も良い使用法は、LINQが任意のシーケンスに対して動作できるようにするIEnumerable<T>
(およびIQueryable<T>
)の拡張です:
var query = someOriginalSequence.Where(person => person.Age > 18)
.OrderBy(person => person.Name)
.Select(person => person.Job);
(またはもちろん、何でも)。拡張メソッドを使用すると、返されるのと同じ型を取る静的メソッドへの呼び出しを効果的に連結できるため、これはすべて実行可能です。
これらはプラグインに非常に便利です。たとえば、プロジェクトで特定のデータ構造を定義し、特定の方法でディスクに保存するとします。しかし、他のプログラムは非常によく似た処理を行いますが、データファイルは異なります。しかし、多くのユーザーが使用する必要のない機能のために、大量のインポートコードでEXEを肥大化させたくありません。プラグインフレームワークを使用して、インポーターを次のように機能するプラグインに配置できます。
type
TCompetitionToMyClass = class helper for TMyClass
public
constructor Convert(base: TCompetition);
end;
そして、コンバーターを定義します。 1つの注意点:クラス helper はクラス friend ではありません。この手法は、パブリックメソッドとプロパティを通じて新しいTMyClassオブジェクトを完全にセットアップできる場合にのみ機能します。しかし、可能であれば、それは本当にうまくいきます。
あなたが何を呼んでいるのかを初めて覚えた<!> quot; class helpers <!> quot; Objective Cを学んでいたとき。Cocoa(AppleのObjective Cフレームワーク)は、<!> quot; Categories。<!> quot;
と呼ばれるものを使用します。カテゴリを使用すると、サブクラス化せずに独自のメソッドを追加して、既存のクラスを拡張できます。実際、Cocoaは可能な限りサブクラス化を避けることをお勧めします。サブクラスを作成することは理にかなっていますが、多くの場合、カテゴリを使用して回避できます。
Cocoaでカテゴリを使用する良い例は、<!> quot; Key Value Code(KVC)<!> quot;と呼ばれるものです。および<!> quot; Key Value Observing(KVO)。<!> quot;
このシステムは、2つのカテゴリ(NSKeyValueCodingおよびNSKeyValueObserving)を使用して実装されます。これらのカテゴリは、必要なクラスに追加できるメソッドを定義および実装します。たとえば、Cocoaは<!> quot; conformance <!> quot;を追加します。これらのカテゴリを使用して次のようなメソッドをNSArrayに追加することにより、KVC / KVOに追加します。
- (id)valueForKey:(NSString *)key
NSArrayクラスには、このメソッドの宣言も実装もありません。ただし、カテゴリの使用を通じて。 NSArrayクラスでそのメソッドを呼び出すことができます。 KVC / KVOに準拠するためにNSArrayをサブクラス化する必要はありません。
NSArray *myArray = [NSArray array]; // Make a new empty array
id myValue = [myArray valueForKey:@"name"]; // Call a method defined in the category
この手法を使用すると、KVC / KVOサポートを独自のクラスに簡単に追加できます。 Javaインターフェースではメソッド宣言を追加できますが、カテゴリでは実際の実装を既存のクラスに追加することもできます。
GameCatが示すように、TStringsは入力を避けるための良い候補です。
type
TMyObject = class
public
procedure DoSomething;
end;
TMyObjectStringsHelper = class helper for TStrings
private
function GetMyObject(const Name: string): TMyObject;
procedure SetMyObject(const Name: string; const Value: TMyObject);
public
property MyObject[const Name: string]: TMyObject read GetMyObject write SetMyObject; default;
end;
function TMyObjectStringsHelper.GetMyObject(const Name: string): TMyObject;
var
idx: Integer;
begin
idx := IndexOf(Name);
if idx < 0 then
result := nil
else
result := Objects[idx] as TMyObject;
end;
procedure TMyObjectStringsHelper.SetMyObject(const Name: string; const Value:
TMyObject);
var
idx: Integer;
begin
idx := IndexOf(Name);
if idx < 0 then
AddObject(Name, Value)
else
Objects[idx] := Value;
end;
var
lst: TStrings;
begin
...
lst['MyName'] := TMyObject.Create;
...
lst['MyName'].DoSomething;
...
end;
レジストリ内の複数行の文字列にアクセスする必要がありましたか?
type
TRegistryHelper = class helper for TRegistry
public
function ReadStrings(const ValueName: string): TStringDynArray;
end;
function TRegistryHelper.ReadStrings(const ValueName: string): TStringDynArray;
var
DataType: DWord;
DataSize: DWord;
Buf: PChar;
P: PChar;
Len: Integer;
I: Integer;
begin
result := nil;
if RegQueryValueEx(CurrentKey, PChar(ValueName), nil, @DataType, nil, @DataSize) = ERROR_SUCCESS then begin
if DataType = REG_MULTI_SZ then begin
GetMem(Buf, DataSize + 2);
try
if RegQueryValueEx(CurrentKey, PChar(ValueName), nil, @DataType, PByte(Buf), @DataSize) = ERROR_SUCCESS then begin
for I := 0 to 1 do begin
if Buf[DataSize - 2] <> #0 then begin
Buf[DataSize] := #0;
Inc(DataSize);
end;
end;
Len := 0;
for I := 0 to DataSize - 1 do
if Buf[I] = #0 then
Inc(Len);
Dec(Len);
if Len > 0 then begin
SetLength(result, Len);
P := Buf;
for I := 0 to Len - 1 do begin
result[I] := StrPas(P);
Inc(P, Length(P) + 1);
end;
end;
end;
finally
FreeMem(Buf, DataSize);
end;
end;
end;
end;
このコメントを読んだため、これらの使用はお勧めしません:
<!> quot;クラスの最大の問題 ヘルパー、それらを使用するp.o.vから 独自のアプリケーションでは、事実です 指定された唯一のクラスヘルパー クラスはいつでもスコープ内にあります。<!> quot; ... <!> quot;つまり、ヘルパーが2人いる場合 範囲内では、1つだけが認識されます コンパイラによって。あなたは何も手に入れません その他に関する警告またはヒント 隠されている可能性のあるヘルパー。<!> quot;
http://davidglassborow.blogspot.com /2006/05/class-helpers-good-or-bad.html
クラス間で一貫した利用可能なクラスメソッドを作成するためにそれらを使用したのを見ました:特定の<!> quot; type <!> quot; ActiveプロパティとVisibleプロパティだけではありません。