2010-06-29 10 views
4

Je trouve l'extrait de code suivant here:Que fait "gratuit" en Delphi?

with TClipper.Create do 
    try 
    AddPolygon(subject, ptSubject); 
    AddPolygon(clip, ptClip); 
    Execute(ctIntersection, solution); 
    finally 
    free; 
    end 

Juste curieux, qu'est-ce que la déclaration/fonction free (entre finally et end) font ici? Google n'a pas aidé.

+6

Méfiez-vous des déclarations peut conduire à des erreurs très trompeuses. Dites que le code est à l'intérieur d'un autre objet tel que TForm1. Quelle méthode est appelée TClipper.Free ou TForm1.Free. Le avec prend la priorité sur la portée, mais il est commun pour les développeurs de manquer cela et d'obtenir des bogues involontaires. –

+4

@Robert Love: Mais la construction montrée ci-dessus est très standard. –

+2

@Andreas Je ne suis pas d'accord, je le vois de temps en temps mais la plupart du code que je regarde n'utilise jamais cette construction, en raison des problèmes qui peuvent surgir de son utilisation. –

Répondre

23

Le code

with TClipper.Create do 
    try 
    AddPolygon(subject, ptSubject); 
    AddPolygon(clip, ptClip); 
    Execute(ctIntersection, solution); 
    finally 
    free; 
    end 

est un raccourci pour

with TClipper.Create do 
begin 
    try 
    AddPolygon(subject, ptSubject); 
    AddPolygon(clip, ptClip); 
    Execute(ctIntersection, solution); 
    finally 
    free; 
    end; 
end; 

TClipper.Create crée un objet de type TClipper, et renvoie celle-ci et la déclaration with, qui fonctionne comme dans la plupart des langues, vous permet d'accéder les méthodes et les propriétés de cet objet TClipper sans utiliser la syntaxe NameOfObject.MethodOrProperty.

(Un exemple plus simple:

MyPoint.X := 0; 
MyPoint.Y := 0; 
MyPoint.Z := 0; 
MyPoint.IsSet := true; 

peut être simplifié à

with MyPoint do 
begin 
    X := 0; 
    Y := 0; 
    Z := 0; 
    IsSet := true; 
end; 

)

Mais dans votre cas, vous ne devez déclarer un objet TClipper comme une variable, parce que vous le créez et pouvez accéder à ses méthodes et propriétés au moyen de la construction with.

donc votre code est presque equivelant à

var 
    Clipper: TClipper; 

Clipper := TClipper.Create; 
Clipper.AddPolygon(subject, ptSubject); 
Clipper.AddPolygon(clip, ptClip); 
Clipper.Execute(ctIntersection, solution); 
Clipper.Free; 

La première ligne, Clipper := TClipper.Create, crée un objet TClipper.Les trois lignes suivantes fonctionnent avec cet objet, puis Clipper.Free détruit l'objet, libérant de la RAM et éventuellement des ressources de temps et de système d'exploitation de l'UC, utilisées par l'objet TClipper.

Mais le code ci-dessus n'est pas bon, parce que si un occurrs d'erreur (une exception est créée) dans AddPolygon ou Execute, le Clipper.Free ne sera jamais appelé, et si vous avez une fuite de mémoire. Pour éviter cela, Delphi utilise la try...finally...end construction:

Clipper := TClipper.Create; 
try 
    Clipper.AddPolygon(subject, ptSubject); 
    Clipper.AddPolygon(clip, ptClip); 
    Clipper.Execute(ctIntersection, solution); 
finally 
    Clipper.Free; 
end; 

Le code entre finally et end est garanti de fonctionner, même si une exception est créée, et même si vous appelez Exit, entre try et finally. Ce que veut dire Mason, c'est que parfois la construction with peut être une peinture dans le cerveau, à cause de conflits d'identifiants. Par exemple, considérons

MyObject.Caption := 'My test'; 

Si vous écrivez ceci à l'intérieur d'une construction with, à savoir si vous écrivez

with MyObect do 
begin 
    // A lot of code 
    Caption := 'My test'; 
    // A lot of code 
end; 

alors vous pourriez devenir confus. En effet, le plus souvent Caption := modifie la légende du formulaire en cours, mais maintenant, en raison de l'instruction with, il va changer la légende de MyObject à la place.

Pire encore, si

MyObject.Title := 'My test'; 

et MyObject n'a pas de propriété Caption, et vous oubliez cela (et je pense que la propriété est appelée Caption), puis

MyObject.Caption := 'My test'; 

ne sera même pas compiler, alors

with MyObect do 
begin 
    // A lot of code 
    Caption := 'My test'; 
    // A lot of code 
end; 

compilera très bien, mais il ne fera pas ce que tu attends

En outre, construit comme

with MyObj1, MyObj2, ..., MyObjN do 

ou imbriquées with déclarations comme dans

with MyConverter do 
    with MyOptionsDialog do 
    with MyConverterExtension do 
     .. 

peut produire beaucoup de conflits.

la défense de la Déclaration With

Je remarque qu'il ya presque un consensus (au moins dans ce fil) que la déclaration with est plus mal que de bien. Bien que je sois conscient de la confusion potentielle et que j'y suis tombé plusieurs fois, je ne peux pas être d'accord. Une utilisation minutieuse de l'instruction with peut rendre le code beaucoup plus joli. Et cela diminue le risque de confusion dû à "barfcode".

Par exemple:

Comparez

var 
    verdata: TVerInfo; 

verdata := GetFileVerNumbers(FileName); 
result := IntToStr(verdata.vMajor) + '.' + IntToStr(verdata.vMinor) + '.' + IntToStr(verdata.vRelease) + '.' + IntToStr(verdata.vBuild); 

avec

with GetFileVerNumbers(FileName) do 
    result := IntToStr(vMajor) + '.' + IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' + IntToStr(vBuild); 

Il n'y a absolument aucun risque de confusion, et non seulement nous sauvons une variable temporaray dans le dernier cas - il est également beaucoup plus lisible.

Ou qu'en est-ce très, très, code standard:

with TAboutDlg.Create(self) do 
    try 
    ShowModal; 
    finally 
    Free; 
    end; 

exactement où est le risque de confusion? De mon propre code, je pourrais donner des centaines d'autres exemples d'instructions, tout en simplifiant le code.

En outre, comme indiqué ci-dessus, il n'y a aucun risque d'utiliser with, tant que vous savez ce que vous faites. Mais que faire si vous souhaitez utiliser une instruction with avec le MyObject dans l'exemple ci-dessus: puis, à l'intérieur de l'instruction with, Caption est égal à MyObject.Caption. Comment changer la légende du formulaire, alors? Simple!

with MyObject do 
begin 
    Caption := 'This is the caption of MyObject.'; 
    Self.Caption := 'This is the caption of Form1 (say).'; 
end; 

Un autre endroit où avec peut être utile est lorsque vous travaillez avec une propriété ou d'un résultat fonction qui prend un montant non négligeable de temps à exécuter.

Pour travailler avec l'exemple ci-dessus TClipper, supposons que vous avez une liste de TClipper objets avec une méthode lente qui retourne la tondeuse pour un tabsheet particulier.

Idéalement, vous ne devez appeler ce getter qu'une seule fois. Vous pouvez donc utiliser une variable locale explicite ou implicite en utilisant avec.

var 
    Clipper : TClipper; 
begin 
    Clipper := ClipList.GetClipperForTab(TabSheet); 
    Clipper.AddPolygon(subject, ptSubject); 
    Clipper.AddPolygon(clip, ptClip); 
    Clipper.Execute(ctIntersection, solution); 
end; 

OU

begin 
    with ClipList.GetClipperForTab(TabSheet)do 
    begin 
    AddPolygon(subject, ptSubject); 
    AddPolygon(clip, ptClip); 
    Execute(ctIntersection, solution); 
    end; 
end; 

Dans un cas comme celui-ci, ou l'autre méthode ferait, mais dans certaines circonstances, généralement dans un complexe conditionals avec peut être plus clair.

var 
    Clipper : TClipper; 
begin 
    Clipper := ClipList.GetClipperForTab(TabSheet); 
    if (Clipper.X = 0) and (Clipper.Height = 0) and .... then 
    Clipper.AddPolygon(subject, ptSubject); 
end; 

OU

begin 
    with ClipList.GetClipperForTab(TabSheet) do 
    if (X = 0) and (Height = 0) and .... then 
     AddPolygon(subject, ptSubject); 
end; 

En fin de compte est est question de goût personnel. Je vais généralement utiliser un avec avec une portée très étroite, et ne jamais les imbriquer. Utilisé de cette façon, ils sont un outil utile pour réduire code à barres.

+5

Jusqu'à ce que l'IDE puisse comprendre ce que "avec" fait référence, je continuerai à l'éviter. –

+5

Bien sûr, vous voulez dire le * débogueur *, pas l'IDE. L'IDE lui-même n'a aucun problème avec le sens de avec. La partie compilateur de l'EDI compile parfaitement le code "avec" et Code Insight résout correctement les références. C'est seulement le débogueur qui ne comprend pas. – Deltics

+1

IMO le défaut principal de 'with 'est qu'il donne une excuse pour placer le code au mauvais endroit. Si le code à l'intérieur de l'instruction 'with 'était placé à l'intérieur de la construction pour laquelle' with' fournissait un alias, tous les avantages de la brièveté sont atteints, et le code de l'appelant semble encore plus propre. –

7

Free appelle le destructeur de l'objet et libère la mémoire occupée par l'instance de l'objet.

15

Il est un appel à TObject.Free, qui est essentiellement défini comme:

if self <> nil then 
    self.Destroy; 

Il est en cours d'exécution sur l'objet TClipper sans nom créé dans la déclaration with.

Ceci est un très bon exemple de pourquoi vous ne devriez pas utiliser with. Cela tend à rendre le code plus difficile à lire.

+7

Ceci est une construction * très * pratique et standard. De cette façon, vous n'avez pas besoin de démarrer votre procédure/unité avec une grande liste de variables TOpenDialog, TMyConverter, TMyCustomDialog, TMySoundLibrary etc, si vous voulez juste utiliser quelques méthodes quelque part. –

+6

+1 J'ai ajouté 'with' à ma liste d'Anti-Patterns Delphi :) – mjn

+2

@mjustin: Je comprends les erreurs possibles (imprévues) et les objections contre l'instruction with, mais il y a des exemples où cela améliore réellement la lisibilité du code et empêche les instructions variables très longues (ce serait bien si nous pouvions déclarer des variables partout comme dans C bien que les puristes de la langue ne l'aimeraient probablement pas non plus). – Remko

3

Je ne sais rien à propos de Delphi mais je suppose qu'il libère les ressources utilisées par TClipper un peu comme une instruction using en C#. C'est juste une conjecture ....

+2

Ouais, c'est essentiellement ça. –

0

Tout objet créé dinamicly doit appeler libre pour libérer à la création alocated mémoire après utilisation. L'objet TClipper est un outil de création, de capture et de gestion de contenu de bureau. C'est donc une sorte d'objet de connexion Delphi avec Clipper. Le créer (création d'objet) est géré dans try finaly end; statment que signifie, si la connexion avec Clipper n'est pas réussie l'objet TClipper ne sera pas créé et ne peut pas être libéré après après de try finaly end; déclaration.

0

Si « avec » est aussi mauvais que des affiches suggèrent, pourraient-ils expliquer s'il vous plaît
1. pourquoi Borland a créé cette construction linguistique et
2. pourquoi ils (Borland/Embarcadero/CodeGear) utilisent largement dans leur propre code?
Bien que je comprenne certainement que certains programmeurs Delphi n'aiment pas "avec", et tout en reconnaissant que certains utilisateurs en abusent, je pense qu'il est idiot de dire "vous ne devriez pas l'utiliser".
angusj - auteur du code incriminé :)

+2

Borland n'a pas créé le langage de construction. Ils ont hérité de Wirth. Et ce n'était pas aussi gros qu'un problème avant le bond Pascal -> Pascal Objet, car à l'époque, le «soi» implicite de l'être dans une méthode n'existait pas. Vous pouvez considérer toutes les méthodes comme étant enveloppées dans un bloc 'with self do do, et une fois que vous avez * deux * couches de portée implicites, vous pouvez entrer dans des problèmes de résolution très complexes qui introduisent des bogues et rendent le code plus difficile à corriger. –

+2

Mason, je suis entièrement d'accord que "avec" peut, et très souvent est abusé au point que ce n'est pas clair ou même trompeuse quant à ce que le propriétaire de certaines méthodes/propriétés/variables sont. Toutefois, lorsqu'il est utilisé dans * très petit * blocs de code, il n'y a pas d'ambiguïté et à mon humble avis "avec" améliore réellement la clarté. –