2008-11-01 4 views
28

Delphi 2009, parmi des trucs sympas, a aussi des méthodes Anonymous. J'ai vu les exemples, et les articles de blog concernant les méthodes anonymes, mais je ne les ai pas encore. Quelqu'un peut-il expliquer pourquoi je devrais être excité?Quelqu'un peut-il m'expliquer les méthodes anonymes?

Répondre

11

Il suffit de penser le code de rappel typique où vous avez besoin d'avoir des données disponibles pour le rappel. Souvent, ces données sont nécessaires pour le rappel seulement, mais vous devez passer par un certain nombre de cerceaux pour y arriver sans avoir à se résigner à des pratiques non-OOP-friendly comme les variables globales. Avec des méthodes anonymes, les données peuvent rester là où elles sont: vous n'avez pas besoin d'étendre inutilement leur portée ou de les copier dans un objet auxiliaire. Il suffit d'écrire votre code de rappel sur place en tant que méthode anonyme et il peut accéder et manipuler toutes les variables locales sur le site où la méthode anonyme est définie (et non là où elle est appelée!).

Il existe d'autres aspects des méthodes anonymes, plus évidemment le fait qu'ils sont, bien: anonyme, mais c'est celui qui a vraiment les fait aller « clic » pour moi ...

+1

Ceci est le commentaire qui m'a fait aller 'AHA'. Pas encore 100% excité mais au moins je peux voir une utilisation dans le monde réel pour le moment. – Steve

1

Je suppose (je ne sais pas Delphi) que cela implique que vous pouvez créer des fonctions comme une sorte d'objet de données maintenant. Cela signifie que vous pouvez, par exemple, transmettre des fonctions en tant que paramètres à d'autres fonctions. Exemple: Une fonction de tri peut prendre en paramètre une fonction de comparaison, étant ainsi beaucoup plus polyvalente.

+1

partiellement. Delphi a déjà des pointeurs de fonction. Mais ils peuvent maintenant être créés anonieusement. Voir mon commentaire –

15

Veuillez jeter un coup d'œil à closures.

Les fonctions anonymes de Delphi sont des fermetures.

Ceux-ci sont créés au sein d'autres fonctions et, en tant que tels, ont accès à la portée de cette fonction. Cela est encore le cas si la fonction anonale est affectée à un paramètre de fonction appelé après l'appel de la fonction d'origine. (Je vais créer un exemple dans un instant).

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; 

Le procédé ci-dessus attribue deux fonctions anonymes pour les champs F1 et F2. Le premier augmente la variable locale et le second montre la valeur de la variable.

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

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

Vous pouvez maintenant appeler les deux fonctions, et elles accèdent au même. Donc, appeler F1 deux fois et F2 une fois montre un 3. Bien sûr, ceci est un exemple simple. Mais il peut être étendu à un code plus utile.

Dans l'environnement multi-thread, des fonctions anonymes peuvent être utilisées dans un appel à Synchronize, ce qui élimine le besoin d'innombrables méthodes.

+1

ok, mais je ne suis toujours pas excité! Génériques Je suis excité parce que je peux voir des centaines d'endroits où je peux les utiliser. Les méthodes anonymes me laissent toujours froid. – Steve

+1

Vous n'avez pas à être excité, vous trouverez une utilisation pour eux ;-). –

+0

Peut-être excité n'est pas le bon mot, mais ce que je veux dire c'est que je ne peux pas penser à un exemple du monde réel (encore) où je dirais "Oui, c'est juste ce que sont les méthodes anaérobies" – Steve

11

Peut être cet exemple peut être d'une certaine valeur pour vous. Ici, je vais mettre en œuvre une liste d'affichage zoomable pour dessiner sur un TCanvas sans déclarer différents types de classes d'affichage. Il fait également un usage intensif des génériques. Supposons que nous avons un TForm avec un TPaintBox et un TTrackBar sur elle ...

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

Les gens ont déjà fourni le code, donc je vais énumérer quelques endroits où ils peuvent être utiles.

Disons que vous avez du code GUI. Normalement, pour quelque chose comme le gestionnaire onclick d'un bouton, vous devez fournir une fonction qui sera appelée quand ce bouton est cliqué. Cependant, disons que toute cette fonction doit faire quelque chose de simple comme faire apparaître une boîte de message ou définir un champ quelque part. Disons que vous avez des dizaines de ces boutons dans votre code. Sans fonctions anonymes, vous devrez avoir des tonnes de fonctions appelées "OnButton1Click", "OnExitButtonClick", etc, qui vont probablement encombrer votre code ... ou vous pouvez créer des fonctions anonymes qui attachent immédiatement à ces événements, et vous don Ne t'inquiète plus pour eux.

Une autre utilisation est la programmation fonctionnelle. Dites que vous avez une liste de nombres. Vous ne voulez récupérer que les nombres divisibles par trois.Il y a probablement une fonction appelée filter qui prend une fonction qui retourne un booléen et une liste, et retourne une nouvelle liste contenant seulement les éléments de la première liste qui, une fois passée à la fonction, a renvoyé True. Exemple:

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

Ce serait gênant d'être forcé de définir une fonction « isDivisibleByThree », puis passer à filtrer, donc une autre utilisation pour les fonctions anonymes ici serait juste de créer rapidement une fonction ne sera pas besoin d'ailleurs et passez-le à filtrer.

Questions connexes