31

Delphi 2009 には、いくつかの優れた機能がありますが、Anonymous メソッドも追加されました。匿名メソッドに関する例とブログ投稿を見てきましたが、まだわかりません。なぜ私が興奮する必要があるのか​​ 誰か説明できますか?

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 に 2 つの無名関数を割り当てます。1 つ目はローカル変数を増加させ、2 つ目は変数の値を表示します。

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

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

これで両方の関数を呼び出すことができ、それらは同じ a にアクセスします。したがって、F1 を 2 回呼び出し、F2 を 1 回呼び出すと、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」などと呼ばれる関数が大量に必要になり、コードが乱雑になる可能性があります...または、これらのイベントにすぐにアタッチする匿名関数を作成できます。もう心配する必要はありません。

もう 1 つの用途は、関数型プログラミングです。数字のリストがあるとします。3 で割り切れる数だけを取得したいとします。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 に答える