Шаг 145.
Указатели на методы и делегирование

    На этом шаге мы рассмотрим делегирование методов.

    Указатели на методы описываются так же, как процедурные типы, которые рассмотрены в шаге 32. Единственным отличием является указание ключевых слов of object после списка формальных параметров. При этом директива дальнего вызова не требуется ни для 16-ти, ни для 32-х разрядной версий Delphi.

type
  TMyMethod = procedure (Sender : Object) of object;

    Для демонстрации общих черт и отличий работы с процедурными типами и типами методов приведем пример, в котором используются те же функции, что и в примере для процедурных типов (см. шаг 32).

unit Unit2;
{ Прообраз стандартной библиотеки пользователя }

interface

type
  TMyFunc = function ( X : Integer ) : Real of object;

  TMyClass1 = class
    private
      FField : Real;
      FMyFunc: TMyFunc;
    protected
      function FirstFunc ( X : Integer ) : Real;
      procedure SetField ( Value : Real );
    public
      procedure TakeAndSet ( X : Integer );
      property Field : Real
        read FField  write SetField;
      property MyFunc : TMyFunc
        read FMyFunc write FMyFunc;
  end;

var
  MyObject1 : TMyClass1;

implementation

function TMyClass1.FirstFunc ( X : Integer ) : Real;
begin
  Result := SQR ( X )/2;
end;

procedure TMyClass1.SetField ( Value : Real );
begin
  FField := Value
end;

procedure TMyClass1.TakeAndSet ( X : Integer );
begin
  SetField (MyFunc(X));
end;

initialization
  MyObject1 := TMyClass1.Create;
  MyObject1.MyFunc := MyObject1.FirstFunc;

finalization
  MyObject1.Free;

end.
unit Unit1;
{ Приложение, использующее стандартный модуль Unit2      }
{ и выполняющее делегирование "своего" метода SecondFunc }
{ импортируемому из Unit2 объекту MyObject1              }
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Unit2;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyClass2 = class
      function SecondFunc ( X : Integer ) : Real;
  end;


var
  Form1: TForm1;
  MyObject2 : TMyClass2;

implementation

{$R *.dfm}

function TMyClass2.SecondFunc ( X : Integer ) : Real;
begin
  Result := SQRT ( X )*2;
end;


procedure TForm1.FormCreate(Sender: TObject);
var
  St: String;
begin
  with MyObject1 do
  begin
   TakeAndSet(16); {Устанавливает  значение  128}
   Str (Field:7:2, St);
   Edit1.Text := St;
   MyObject2:= TMyClass2.Create;
   MyFunc:= MyObject2.SecondFunc;  { Делегирование }
   TakeAndSet(16); { Устанавливает значение 8}
   Str (Field:7:2, St);
   Edit2.Text:= St;
   MyObject2.Free;
  end;
end;

end.
Текст этого примера можно взять здесь.

    Результат:


Рис.1. Результат работы приложения

    В этом примере описаны два модуля: Unit2 содержит прообраз библиотеки пользователя, a Unit1 - прообраз приложения, использующего эту библиотеку. В модуле Unit2 объявлен класс TMyClass1, содержащий свойство MyFunc процедурного типа, и объект этого класса MyObject1, который в инициализационной части модуля стандартным образом настраивается на использование своего внутреннего метода FirstFunc. В результате метод TakeAndSet класса TMyClass1, который делает вызов метода, присвоенного свойству MyFunc, по умолчанию будет вызывать FirstFunc. Однако благодаря процедурному типу свойства MyFunc в приложении, использующем библиотеку, метод TakeAndSet можно перенастроить так, что он будет вызывать метод SecondFunc из класса TMyClass2, который совместим по типу с методом FirstFunc. Такая передача действия, описанного в одном классе, методу из другого класса называется делегированием. В данном примере делегирование выполняется оператором:

    MyFunc := MyVar2.SecondFunc;

    Поскольку события Delphi реализованы в виде процедурных свойств, которые можно изменять во время работы программы, то для них также применимо делегирование. Чтобы делегировать свой метод стандартному событию Delphi, нужно знать вид заголовка метода, который обрабатывает это событие. Например, тип TNotifyEvent описан для событий, которые не имеют дополнительных параметров.

type
  TNotifyEvent = procedure(Sender: TObject)   of  object;

    Описанный в типе единственный параметр Sender является стандартным параметром всех обработчиков событий.

    На следующем шаге мы рассмотрим классовые методы.




Предыдущий шаг Содержание Следующий шаг