tags:

views:

376

answers:

6

I found the following code snippet here:

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

Just curious, what does the free statement/function (between finally and end) do here? Google did not help.

+6  A: 

Free calls the destructor of the object, and releases the memory occupied by the instance of the object.

John Nolan
+14  A: 

It's a call to TObject.Free, which is basically defined as:

if self <> nil then
  self.Destroy;

It's being executed on the unnamed TClipper object created in the with statement.

This is a very good example of why you shouldn't use with. It tends to make the code harder to read.

Mason Wheeler
This is a *very* convenient and standard construct. This way you do not need to start your procedure/unit with a large list of TOpenDialog, TMyConverter, TMyCustomDialog, TMySoundLibrary variables etc, if you just want to use a few methods somewhere.
Andreas Rejbrand
+1 I have added `with` to my list of Delphi Anti-Patterns :)
mjustin
@mjustin: I understand the possible (unforeseen) errors and the objections against the with statement, yet there are some examples where it actually improves code readability and prevents very lengthy variable statements (it would be nice if we could declare variables everywhere just like in C although language purists probably won't like that either).
Remko
The code is perfectly easy to read. The poster didn't know what "Free" does and didn't know how "with" worked. Eliminating the use of "with" addressed only one HALF of that understanding gap.
Deltics
+1 for the answer to what does <code>free</code> do but for saying that's an example for why you shouldn't use <code>with</code> I'd give -1. the only "shouldn't" I would use is you shouldn't use tools you don't fully understand.
pastacool
+3  A: 

I don't know anything about Delphi but I would assume that it is releasing the resources used by TClipper much like a using statement in C#. That is just a guess....

runxc1 Bret Ferrier
Yeah, that's basically it.
Mason Wheeler
A: 

Any dinamicly created object must call free to free at object creation alocated memory after use. TClipper object is a desktop content creation, capture and management tool. So it is some kind of Delphi connection object with Clipper. The create (object creation) is handled in try finaly end; statment what mean, if connection with Clipper isn't successful the object TClipper will not be created and can not be freed after after of try finaly end; statement.

GJ
+19  A: 

The code

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

is shorthand for

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

TClipper.Create creates an object of type TClipper, and returns this, and the with statement, which works as in most languages, lets you access the methods and properties of this TClipper object without using the NameOfObject.MethodOrProperty syntax.

(A simpler example:

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

can be simplified to

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

)

But in your case, you never need to declare a TClipper object as a variable, because you create it and can access its methods and properties by means of the with construct.

So your code is almost equivelant to

var
  Clipper: TClipper;

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

The first line, Clipper := TClipper.Create, creates a TClipper object. The following three lines work with this object, and then Clipper.Free destroys the object, freeing RAM and possibly also CPU time and OS resources, used by the TClipper object.

But the above code is not good, because if an error occurrs (an exception is created) within AddPolygon or Execute, then the Clipper.Free will never be called, and so you have a memory leak. To prevent this, Delphi uses the try...finally...end construct:

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

The code between finally and end is guaranteed to run, even if an exception is created, and even if you call Exit, between try and finally.

What Mason means is that sometimes the with construct can be a paint in the ... brain, because of identifier conflicts. For instance, consider

MyObject.Caption := 'My test';

If you write this inside a with construct, i.e. if you write

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

then you might get confused. Indeed, most often Caption := changes the caption of the current form, but now, due to the with statement, it will change the caption of MyObject instead.

Even worse, if

MyObject.Title := 'My test';

and MyObject has no Caption property, and you forget this (and think that the property is called Caption), then

MyObject.Caption := 'My test';

will not even compile, whereas

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

will compile just fine, but it won't do what you expect.

In addition, constructs like

with MyObj1, MyObj2, ..., MyObjN do

or nested with statements as in

with MyConverter do
  with MyOptionsDialog do
    with MyConverterExtension do
      ..

can produce a lot of conflicts.

In Defence of The With Statement

I notice that there almost is a consensus (at least in this thread) that the with statement is more evil than good. Although I am aware of the potential confusion, and have fallen for it a couple of times, I cannot agree. Careful use of the with statement can make the code look much prettier. And this lessens the risk of confusion due to "barfcode".

For example:

Compare

var
  verdata: TVerInfo;

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

with

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

There is absolutely no risk of confusion, and not only do we save a temporaray variable in the last case - it also is far more readable.

Or what about this very, very, standard code:

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

Exactly where is the risk of confusion? From my own code I could give hundreds of more examples of with statements, all simplifying code.

Furthermore, as have been stated above, there is no risk of using with at all, as long as you know what you are doing. But what if you want to use a with statement together with the MyObject in the example above: then, inside the with statement, Caption is equal to MyObject.Caption. How do you change the caption of the form, then? Simple!

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

Another place where with can be useful is when working with a property or function result that takes a non-trivial amount of time to execute.

To work with the TClipper example above, suppose that you have a list of TClipper objects with a slow method that returns the clipper for a particular TabSheet.

Ideally you should only call this getter once, so you can either use an explicit local variable, or an implicit one using with.

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

OR

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

In a case like this, either method would do, but in some circumstances, typically in complex conditionals a with can be clearer.

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

OR

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

In the end is is matter of personal taste. I generally will only use a with with a very tight scope, and never nest them. Used this way they are a useful tool to reduce barfcode.

Andreas Rejbrand
Until the IDE can figure out what "with" refers to, I will continue to avoid it.
Chris Thornton
Of course, you mean the *debugger*, not the IDE. The IDE itself has no trouble with the meaning of with. The compiler part of the IDE compiles "with" code perfectly accurately and Code Insight resolves references correctly. It's only the debugger that doesn't get it right.
Deltics
IMO the primary shortcoming of `with` is that it gives an excuse for placing code in the wrong place. If the code inside the `with` statement were instead placed inside the construct for which `with` is providing an alias, all the same benefits of brevity are achieved, and the caller code looks even cleaner.
cjrh
@cjrh: Well, I would say that this applies to far less than 50 % of my uses of `with`, most of which are records and it would not make any sense to add the code to the record itself.
Andreas Rejbrand
Adding methods to records is a bundling of state and action. It is done for exactly the same reason as for classes. If your code segment requires manipulation of so many different fields of the record as to justify a `with` statement, I think it is reasonable to ask whether it may not be better to have the record act on itself instead, via a method call.
cjrh
@cjrh: I meant that I *read* the record's members, not write to them.
Andreas Rejbrand
In defense of the people who dislike with, I find a one letter local variable name beats a with statement for readability, maintainability, debug-ability (is that a word?), and clarity of scope.
Warren P
A: 

If "with" is as evil as some posters are suggesting, could they please explain
1. why Borland created this language construct, and
2. why they (Borland/Embarcadero/CodeGear) use it extensively in their own code?
While I certainly understand that some Delphi programmers don't like "with", and while acknowledging that some users abuse it, I think it's silly to say "you shouldn't use it".
angusj - author of the offending code :)

Angus Johnson
Borland didn't create the language construct. They inherited it from Wirth. And it wasn't nearly as big of a problem before the Pascal -> Object Pascal leap, because back then the implicit `self` scoping of being inside a method didn't exist. You can consider all methods as being wrapped in a `with self do` block, and once you have *two* implicit scope layers, you can get into some really tricky resolution issues that introduce bugs and make the code harder to fix.
Mason Wheeler
Mason, I will heartily agree that "with" can, and very often is abused to the point that it's unclear or even misleading as to what the owner of certain methods/properties/variables are. However, when used in *very small* code blocks, there is no ambiguity and IMHO "with" actually improves clarity.
Angus Johnson