Что хорошего для классовых помощников?

В Delphi (и, вероятно, много других языков) есть помощники класса. Они обеспечивают способ добавления дополнительных методов к существующему классу. Без создания подкласса.

Итак, что полезно для помощников классов?

Ответ 1

Я использую их:

  • Чтобы вставить перечисления в классы 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 }
    
  • Чтобы упростить доступ к полям записи и удалить кастинг.

Ответ 2

Сначала я был скептически относился к помощникам класса. Но затем я прочитал интересную запись в блоге, и теперь я убежден, что они действительно полезны.

Например, если вам нужна дополнительная функциональность для существующего класса экземпляра, и по какой-то причине вы не можете изменить существующий источник. Вы можете создать помощник класса, чтобы добавить эту функциональность.

Пример:

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;

Примечания:

  • Помощники класса могут храниться в отдельном блоке, поэтому вы можете добавить своих собственных помощников класса. Не забудьте дать этим устройствам легко запоминающееся имя, например ClassesHelpers, для помощников для модуля Classes.
  • Есть также помощники записи.
  • Если в области есть несколько помощников классов, ожидайте некоторые проблемы, можно использовать только один помощник.

Ответ 3

Это очень похоже на методы расширения в С# 3 (и VB9). Лучшее использование, которое я видел для них, - это расширения для IEnumerable<T>IQueryable<T>), которые позволяют LINQ работать с произвольными последовательностями:

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

(или что-то еще, конечно). Все это выполнимо, поскольку методы расширения позволяют эффективно объединять вызовы статическим методам, которые принимают тот же тип, что и они.

Ответ 4

Они очень полезны для плагинов. Например, пусть ваш проект определяет определенную структуру данных и сохраняет ее на диске определенным образом. Но тогда какая-то другая программа делает что-то очень похожее, но файл данных отличается. Но вы не хотите раздувать ваш EXE с кучей кода импорта для функции, которую многие ваши пользователи не будут использовать. Вы можете использовать платформу плагина и поместить импортеров в плагин, который будет работать следующим образом:

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

И затем определите конвертер. Одно предостережение: помощник класса не является другом класса. Этот метод будет работать только в том случае, если можно полностью настроить новый объект TMyClass с помощью своих общедоступных методов и свойств. Но если вы можете, он работает очень хорошо.

Ответ 5

В первый раз, когда я помню, что вы испытывали то, что вы называете "помощниками класса", было при изучении Objective C. Cocoa (инфраструктура Apple Objective C) использует так называемые "категории".

Категория позволяет расширить существующий класс, добавив собственные методы без подкласса. Фактически Cocoa рекомендует вам избегать подклассов, когда это возможно. Часто это имеет смысл для подкласса, но часто его можно избежать с помощью категорий.

Хорошим примером использования категории в Cocoa является то, что называется "Key Value Code (KVC)" и "Key Value Observing (KVO)".

Эта система реализована с использованием двух категорий (NSKeyValueCoding и NSKeyValueObserving). Эти категории определяют и реализуют методы, которые могут быть добавлены в любой класс, который вы хотите. Например, Cocoa добавляет "соответствие" KVC/KVO с помощью этих категорий для добавления методов в NSArray, таких как:

- (id)valueForKey:(NSString *)key

Класс NSArray не имеет ни декларации, ни реализации этого метода. Однако, используя категорию. Вы можете вызвать этот метод для любого класса NSArray. Вам не требуется подкласс NSArray для получения соответствия KVC/KVO.

NSArray *myArray = [NSArray array]; // Make a new empty array
id myValue = [myArray valueForKey:@"name"]; // Call a method defined in the category

Используя этот метод, вы легко можете добавить поддержку KVC/KVO в свои собственные классы. Интерфейсы Java позволяют добавлять объявления методов, но категории позволяют также добавлять фактические реализации в существующие классы.

Ответ 6

Как показывает 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;

Ответ 7

Я бы не рекомендовал их использовать, так как я прочитал этот комментарий:

"Самая большая проблема с классом помощники, из p.o.v их использования в ваших собственных приложениях, это факт что только один помощник класса для данного класс может быть в любой момент".... "То есть, если у вас есть два помощника в рамках, будет признан только ОДИН компилятором. Вы не получите никакой предупреждения или даже намеки на любые другие которые могут быть скрыты.

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

Ответ 8

Я видел, как они использовались для создания доступных методов класса, согласованных между классами: Добавление Open/Close и Show/Hide ко всем классам данного типа, а не только к активным и видимым свойствам.