37

Delphi (およびおそらく他の多くの言語) には、クラス ヘルパーがあります。これらは、既存のクラスに追加のメソッドを追加する方法を提供します。サブクラスを作成せずに。

では、クラス ヘルパーにはどのような用途があるでしょうか。

4

9 に答える 9

34

私はそれらを使用しています:

  • 列挙子を実装していない VCL クラスに列挙子を挿入します。
  • VCL クラスを拡張します。
  • TStrings クラスにメソッドを追加して、派生リストと 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 }
    
  • レコード フィールドへのアクセスを簡素化し、キャストを削除します。

于 2008-10-31T13:30:44.677 に答える
14

最初は、クラス ヘルパーについて懐疑的でした。しかし、興味深いブログ エントリを読んで、今ではそれらが実際に役立つと確信しています。

たとえば、既存のインスタンス クラスに追加機能が必要で、何らかの理由で既存のソースを変更できない場合です。この機能を追加するクラス ヘルパーを作成できます。

例:

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 つだけです。
于 2008-10-31T13:07:22.867 に答える
6

これは、C#3 (および VB9) の拡張メソッドと非常によく似ています。私が見た中で最もよく使われているのは、LINQ が任意のシーケンスに対して動作できるようにするIEnumerable<T>(and ) の拡張機能です。IQueryable<T>

var query = someOriginalSequence.Where(person => person.Age > 18)
                                .OrderBy(person => person.Name)
                                .Select(person => person.Job);

(またはもちろん、何でも)。拡張メソッドを使用すると、戻り値と同じ型を取る静的メソッドへの呼び出しを効果的に連鎖できるため、これらすべてが実行可能です。

于 2008-10-31T13:18:30.160 に答える
4

それらはプラグインに非常に役立ちます。たとえば、プロジェクトが特定のデータ構造を定義し、特定の方法でディスクに保存されているとします。しかし、他のプログラムが非常に似たようなことを行いますが、データ ファイルは異なります。しかし、多くのユーザーが使用する必要のない機能のために、一連のインポート コードで EXE を肥大化させたくはありません。プラグイン フレームワークを使用して、次のように機能するプラグインにインポーターを配置できます。

type
   TCompetitionToMyClass = class helper for TMyClass
   public
      constructor Convert(base: TCompetition);
   end;

次に、コンバーターを定義します。1 つの注意点: クラスヘルパーはクラスフレンドではありません。この手法は、パブリック メソッドとプロパティを使用して新しい TMyClass オブジェクトを完全にセットアップできる場合にのみ機能します。しかし、それができれば、それは非常にうまく機能します。

于 2008-10-31T20:37:49.713 に答える
3

あなたが「クラス ヘルパー」と呼んでいるものを初めて経験したのは、Objective C を学習しているときでした。Cocoa (Apple の Objective C フレームワーク) は、「カテゴリ」と呼ばれるものを使用します。

カテゴリを使用すると、サブクラス化せずに独自のメソッドを追加して、既存のクラスを拡張できます。実際、Cocoa は可能な限りサブクラス化を避けることを推奨しています。多くの場合、サブクラス化は理にかなっていますが、多くの場合、カテゴリを使用して回避できます。

Cocoa でのカテゴリの使用の良い例は、「キー バリュー コード (KVC)」および「キー バリュー オブザービング (KVO)」と呼ばれるものです。

このシステムは、2 つのカテゴリ (NSKeyValueCoding と NSKeyValueObserving) を使用して実装されます。これらのカテゴリは、任意のクラスに追加できるメソッドを定義および実装します。たとえば、Cocoa は、これらのカテゴリを使用して次のようなメソッドを 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 インターフェイスを使用するとメソッド宣言を追加できますが、カテゴリを使用すると、実際の実装を既存のクラスに追加することもできます。

于 2008-10-31T14:10:26.873 に答える
3

このコメントを読んだので、それらを使用することはお勧めしません。

「クラス ヘルパーの最大の問題は、独自のアプリケーションでクラス ヘルパーを使用するという観点からすると、特定のクラスに対して常に 1 つのクラス ヘルパーしかスコープ内に存在しない可能性があるという事実です。」... 「つまり、スコープ内に 2 つのヘルパーがある場合、コンパイラによって認識されるのは 1 つだけです。非表示になっている可能性のある他のヘルパーに関する警告やヒントさえも得られません。」

http://davidglassborow.blogspot.com/2006/05/class-helpers-good-or-bad.html

于 2009-07-04T05:57:25.870 に答える
3

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;
于 2008-10-31T14:27:36.230 に答える
0

利用可能なクラスメソッドをクラス間で一貫させるために使用されるのを見てきました。ActiveプロパティとVisibleプロパティだけでなく、特定の「タイプ」のすべてのクラスにOpen/CloseとShow/Hideを追加します。

于 2009-01-27T11:32:59.347 に答える
0

Dephi が拡張メソッドをサポートしている場合、私が望む用途の 1 つは次のとおりです

TGuidHelper = class
public
   class function IsEmpty(this Value: TGUID): Boolean;
end;

class function TGuidHelper(this Value: TGUID): Boolean;
begin
   Result := (Value = TGuid.Empty);
end;

だから私は呼び出すことができますif customerGuid.IsEmpty then ...

IDataRecordもう 1 つの良い例は、パラダイム (私が大好きです) を使用して XML ドキュメント (または、そのようなことに興味がある場合は JSON) から値を読み取ることができることです。

orderGuid := xmlDocument.GetGuid('/Order/OrderID');

どちらがはるかに優れています:

var
   node: IXMLDOMNode;

   node := xmlDocument.selectSingleNode('/Order/OrderID');
   if Assigned(node) then
      orderID := StrToGuid(node.Text) //throw convert error on empty or invalid
   else
      orderID := TGuid.Empty; // "DBNull" becomes the null guid
于 2021-11-03T15:26:39.730 に答える