Может кто-нибудь объяснить мне анонимные методы? - PullRequest
28 голосов
/ 02 ноября 2008

Delphi 2009, среди некоторых интересных вещей, также только что получил анонимные методы. Я видел примеры и посты в блоге, касающиеся анонимных методов, но пока не получил их. Может кто-нибудь объяснить, почему я должен быть взволнован?

Ответы [ 7 ]

16 голосов
/ 02 ноября 2008

Пожалуйста, посмотрите на замыкания .

Анонимные функции Delphi являются замыканиями.

Они создаются в других функциях и поэтому имеют доступ к области действия этой функции. Это даже в том случае, если анонимная функция назначается параметру функции, который вызывается после вызова исходной функции. (Я создам пример через минуту).

type
  TAnonFunc = reference to procedure;
  TForm2 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    F1 : TAnonFunc;
    F2 : TAnonFunc;
  end;

procedure TForm2.Button1Click(Sender: TObject);
var
  a : Integer;
begin
  a := 1;

  F1 := procedure
  begin
    a := a + 1;
  end;

  F2 := procedure
  begin
    Memo1.Lines.Add(IntToStr(a));
  end;
end;

Приведенный выше метод назначает две анонимные функции полям F1 и F2. Первый увеличивает локальную переменную, а второй показывает значение переменной.

procedure TForm2.Button2Click(Sender: TObject);
begin
  F1;
end;

procedure TForm2.Button3Click(Sender: TObject);
begin
  F2;
end;

Теперь вы можете вызывать обе функции, и они получают доступ к одному и тому же. Поэтому, если дважды вызвать F1, а F2 - 3. Конечно, это простой пример. Но его можно расширить до более полезного кода.

В многопоточной среде анонимные функции могут использоваться при вызове Synchronize, что устраняет необходимость в бесчисленных методах.

12 голосов
/ 05 ноября 2008

Подумайте только о типичном коде обратного вызова, в котором вам нужны данные, доступные для обратного вызова. Зачастую эти данные необходимы для обратного вызова только , однако вам нужно перепрыгнуть через несколько обручей, чтобы получить их там, не прибегая к отказоустойчивым методам, таким как глобальные переменные. С помощью анонимных методов данные могут оставаться там, где они есть - вам не нужно излишне расширять область действия или копировать их в какой-то вспомогательный объект. Просто напишите свой код обратного вызова на месте как анонимный метод, и он сможет полностью получить доступ и управлять всеми локальными переменными на сайте, где определен анонимный метод (а не там, где он вызывается!).

Существуют и другие аспекты анонимных методов, наиболее очевидно тот факт, что они, ну, в общем-то, анонимные, но это тот, который действительно заставил их пойти «щелкнуть» для меня ...

11 голосов
/ 02 ноября 2008

Может быть, этот пример может быть для вас полезным. Здесь я собираюсь реализовать масштабируемый список отображения для рисования на TCanvas без объявления различных типов классов отображения. Это также интенсивно использует Дженерики. Предположим, у нас есть TForm с TPaintBox и TTrackBar на нем ...

type
  TDisplayProc = TProc<TCanvas>;

type
  TFrmExample3 = class(TForm)
    pbxMain: TPaintBox;
    trkZoom: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure pbxMainClick(Sender: TObject);
    procedure pbxMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure pbxMainPaint(Sender: TObject);
    procedure trkZoomChange(Sender: TObject);
  private
    FDisplayList: TList<TDisplayProc>;
    FMouseX: Integer;
    FMouseY: Integer;
    FZoom: Extended;
    procedure SetZoom(const Value: Extended);
  protected
    procedure CreateCircle(X, Y: Integer);
    procedure CreateRectangle(X, Y: Integer);
    function MakeRect(X, Y, R: Integer): TRect;
  public
    property Zoom: Extended read FZoom write SetZoom;
  end;

implementation

{$R *.dfm}

procedure TFrmExample3.PaintBox1Paint(Sender: TObject);
var
  displayProc: TDisplayProc;
begin
  for displayProc in FDisplayList do
    displayProc((Sender as TPaintBox).Canvas);
end;

procedure TFrmExample3.CreateCircle(X, Y: Integer);
begin
  FDisplayList.Add(
    procedure (Canvas: TCanvas)
    begin
      Canvas.Brush.Color := clYellow;
      Canvas.Ellipse(MakeRect(X, Y, 20));
    end
  );
end;

procedure TFrmExample3.CreateRectangle(X, Y: Integer);
begin
  FDisplayList.Add(
    procedure (Canvas: TCanvas)
    begin
      Canvas.Brush.Color := clBlue;
      Canvas.FillRect(MakeRect(X, Y, 20));
    end
  );
end;

procedure TFrmExample3.FormCreate(Sender: TObject);
begin
  FDisplayList := TList<TDisplayProc>.Create;
end;

procedure TFrmExample3.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FDisplayList);
end;

function TFrmExample3.MakeRect(X, Y, R: Integer): TRect;
begin
  Result := Rect(Round(Zoom*(X - R)), Round(Zoom*(Y - R)), Round(Zoom*(X + R)), Round(Zoom*(Y + R)));
end;

procedure TFrmExample3.pbxMainClick(Sender: TObject);
begin
  case Random(2) of
    0: CreateRectangle(Round(FMouseX/Zoom), Round(FMouseY/Zoom));
    1: CreateCircle(Round(FMouseX/Zoom), Round(FMouseY/Zoom));
  end;
  pbxMain.Invalidate;
end;

procedure TFrmExample3.pbxMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  FMouseX := X;
  FMouseY := Y;
end;

procedure TFrmExample4.SetZoom(const Value: Extended);
begin
  FZoom := Value;
  trkZoom.Position := Round(2*(FZoom - 1));
end;

procedure TFrmExample4.trkZoomChange(Sender: TObject);
begin
  Zoom := 0.5*(Sender as TTrackBar).Position + 1;
  pbxMain.Invalidate;
end;
5 голосов
/ 03 ноября 2008

Я отвечаю на свой вопрос, но нашел здесь хорошее объяснение анонимных методов. Может ли ваш язык программирования сделать это?

5 голосов
/ 02 ноября 2008

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

Скажем, у вас есть код GUI. Обычно, для чего-то вроде обработчика нажатия кнопки, вы должны предоставить функцию, которая будет вызываться при нажатии этой кнопки. Однако, скажем, все, что должна делать эта функция, это что-то простое, например, всплывающее окно с сообщением или какое-то поле. Допустим, у вас есть десятки этих кнопок в вашем коде. Без анонимных функций у вас будет множество функций с именами «OnButton1Click», «OnExitButtonClick» и т. Д., Которые, вероятно, загромождают ваш код ... или вы можете создавать анонимные функции, которые немедленно присоединяются к этим событиям. больше не о них беспокоиться.

Другое использование - функциональное программирование. Скажем, у вас есть список номеров. Вы хотите вернуть только те числа, которые делятся на три. Вероятно, есть функция с именем filter, которая принимает функцию, которая возвращает логическое значение и список, и возвращает новый список, содержащий только те элементы в первом списке, которые при передаче в функцию возвращали значение True. Пример:

filter(isOdd, [1, 2, 3, 5, 6, 9, 10]) --> [1, 3, 5, 9]

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

1 голос
/ 02 ноября 2008

Анонимные методы полезны в функциональном программировании, но они также могут помочь вам написать более компактный код в структурированном программировании. Threading, например: http://blogs.codegear.com/abauer/2008/09/08/38868

Еще один вариант использования вашего «волнения» :): http://delphi.fosdal.com/2008/08/anonymous-methods-when-to-use-them.html

1 голос
/ 02 ноября 2008

Полагаю (я не знаю Delphi), это означает, что теперь вы можете создавать функции в качестве объекта данных. Это означает, что вы можете, например, передавать функции в качестве параметров другим функциям. Пример: функция сортировки может принимать функцию сравнения в качестве параметра, что делает ее более универсальной.

...