31

在一些很酷的东西中,Delphi 2009 也刚刚获得了匿名方法。我已经看过这些例子,以及关于匿名方法的博客文章,但我还没有得到它们。有人能解释为什么我应该兴奋吗?

4

7 回答 7

16

请看一下闭包

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;

您现在可以调用这两个函数,并且它们访问相同的 a。所以调用 F1 两次,F2 一次显示 3。当然这是一个简单的例子。但它可以扩展为更有用的代码。

在多线程环境中,可以在调用 Synchronise 时使用匿名函数,这样就不需要无数方法了。

于 2008-11-01T21:54:02.477 回答
13

想想典型的回调代码,你需要有数据可用于回调。通常只有回调需要这些数据,但您必须跳过许多圈才能到达那里,而不必接受全局变量等非 OOP 友好的做法。使用匿名方法,数据可以保留在原处——您不必不必要地扩展其范围或将其复制到某个辅助对象。只需将您的回调代码就地编写为匿名方法,它就可以在定义匿名方法的位置(而不是调用它的位置!)完全访问和操作所有局部变量。

匿名方法还有其他方面,最明显的是它们是,嗯:匿名,但这是真正让他们为我“点击”的那个......

于 2008-11-05T14:00:44.877 回答
11

可能这个例子对你有一些价值。在这里,我将实现一个可缩放的显示列表,用于在 TCanvas 上进行绘制,而无需声明不同类型的显示类。它还大量使用泛型。假设我们有一个带有 TPaintBox 和 TTrackBar 的 TForm...

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;
于 2008-11-02T18:13:35.413 回答
5

人们已经提供了代码,所以我只列出一些有用的地方。

假设您有一些 GUI 代码。通常,对于像按钮的 onclick 处理程序这样的东西,您必须提供一个在单击该按钮时将调用的函数。但是,假设该函数所要做的只是一些简单的事情,比如弹出一个消息框或在某处设置一个字段。假设您的代码中有几十个这样的按钮。如果没有匿名函数,您将不得不拥有大量名为“OnButton1Click”、“OnExitButtonClick”等的函数,这可能会使您的代码混乱……或者您可以创建立即附加到这些事件的匿名函数,而您不需要再也不用担心他们了。

另一个用途是函数式编程。假设您有一个数字列表。你只想取回那些能被三整除的数字。可能有一个函数被调用filter,它接受一个返回布尔值和列表的函数,并返回一个新列表,该列表仅包含第一个列表中的那些元素,当传递给函数时,返回 True。例子:

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

被迫定义一个函数“isDivisibleByThree”,然后将其传递给过滤器会很烦人,因此这里匿名函数的另一个用途是快速创建一个您在其他任何地方都不需要的函数并将其传递给过滤器。

于 2008-11-02T18:24:39.947 回答
5

我正在回答我自己的问题,但我在这里找到了对匿名方法的一个很好的解释 你的编程语言可以做到这一点吗?

于 2008-11-02T23:18:40.307 回答
1

我猜(我不知道 Delphi)这意味着您现在可以将函数创建为一种数据对象。例如,这意味着您可以将函数作为参数传递给其他函数。示例:排序函数可能将比较函数作为参数,因此更加通用。

于 2008-11-01T21:48:58.350 回答
1

匿名方法在函数式编程中很有用,但它们也可以帮助您在结构化编程中编写更紧凑的代码。线程,例如:http: //blogs.codegear.com/abauer/2008/09/08/38868

您的“兴奋”的另一个用例:):http: //delphi.fosdal.com/2008/08/anonymous-methods-when-to-use-them.html

于 2008-11-02T08:02:19.517 回答