Delphi 2009, among some cool stuff, has also just got Anonymous methods. I've seen the examples, and the blog posts regarding anonymous methods, but I don't get them yet. Can someone explain why I should be excited?
问题:
回答1:
Just think of typical callback code where you need to have data available to the callback. Often this data is needed for the callback only, yet you have to jump through a number of hoops to get it there without having to resign to un-OOP-friendly practices like global variables. With anonymous methods the data can stay where it is - you don't have to unnecessarily extend its scope or copy it to some helper object. Just write your callback code in-place as an anonymous method and it can fully access and manipulate all local variables at the site where the anonymous method is defined (not where it's called!).
There are other aspects of anonymous methods, most obviously the fact that they are, well: anonymous, but this is the one that really made them go "click" for me...
回答2:
Please have a look at closures.
Delphi anonymous functions are closures.
These are created within other functions and as such has access to the scope of that function. This is even so if the anonumous function is assigned to a function parameter that is called after the original function is called. (I will create an example in a moment).
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;
The above method assigns two anonymous functions to the fields F1 and F2. The first increases the local variable and the second showe the value of the variable.
procedure TForm2.Button2Click(Sender: TObject);
begin
F1;
end;
procedure TForm2.Button3Click(Sender: TObject);
begin
F2;
end;
You can now call both functions, and they access the same a. So calling F1 twice and F2 once shows a 3. Of course this is a simple example. But it can be expanded to more usefull code.
In the multi threading environment, anonymous functions can be used in a call to Synchronise, which eliminates the need for countless methods.
回答3:
May be this example can be of some value for you. Here I'm going to implement a zoomable display list for drawing on a TCanvas without declaring different types of display classes. It also makes heavy use of Generics. Assume we have a TForm with a TPaintBox and a TTrackBar on it...
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;
回答4:
People have already provided the code, so I'll just list some places where they can be useful.
Say you have some GUI code. Normally, for something like a button's onclick handler, you have to provide a function that will be called when that button is clicked. However, let's say all that function has to do is something simple like pop up a message box or set a field somewhere. Let's say you have dozens of these buttons throughout your code. Without anonymous functions, you'll have to have tons of functions called "OnButton1Click," "OnExitButtonClick," etc, which will likely clutter up your code... or you can create anonymous functions that immediately attach to these events, and you don't have to worry about them anymore.
Another use is functional programming. Say you have a list of numbers. You want to get back only those numbers that are divisible by three. There is likely a function called filter
which takes a function that returns a boolean and a list, and returns a new list containing only those elements in the first list that, when passed to the function, returned True. Example:
filter(isOdd, [1, 2, 3, 5, 6, 9, 10]) --> [1, 3, 5, 9]
It'd be annoying to be forced to define a function "isDivisibleByThree", then pass it to filter, so another use for anonymous functions here would be to just quickly create a function you won't need anywhere else and pass it to filter.
回答5:
I'm answering my own question, but I found a good explanation of anonymous methods here Can your programming language do this?
回答6:
I guess (I don't know Delphi) this implies that you can create functions as a kind of data object now. This means that you can, for example, pass functions as parameters to other functions. Example: A sort function might take a comparison function as a parameter, thus being much more versatile.
回答7:
Anonymous methods are useful in functional programming, but they also can help you to write a more compact code in structured programming. Threading, for example: http://blogs.codegear.com/abauer/2008/09/08/38868
Another use cases for your 'excitement' :) : http://delphi.fosdal.com/2008/08/anonymous-methods-when-to-use-them.html