The code
with TClipper.Create do try AddPolygon(subject, ptSubject); AddPolygon(clip, ptClip); Execute(ctIntersection, solution); finally free; end
is short 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 like in most languages, allows you to access the methods and properties of this TClipper object without using the NameOfObject.MethodOrProperty syntax.
(Simple 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 using the with construct.
So your code is almost equivalent
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 next three lines work with this object, and then Clipper.Free destroys the object, freeing up RAM and, possibly, processor time and OS resources used by the TClipper object.
But the above code is not good, because if an error occurs (an exception is thrown) inside AddPolygon or Execute , then Clipper.Free will never be called, and therefore you will 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;
Code execution is guaranteed between finally and end , even if an exception is thrown, and even if you call Exit , between try and finally .
What Mason means is that sometimes the with construct can be paint in ... the brain due to identity conflicts. For example, consider
MyObject.Caption := 'My test';
If you write this inside the 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 can get confused. In fact, most often, Caption := changes the title of the current form, but now the MyObject title will be changed instead with statement.
Worse, if
MyObject.Title := 'My test';
and MyObject does not have a Caption property, and you forget it (and think the property is called Caption ), then
MyObject.Caption := 'My test';
doesn't 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, designs such as
with MyObj1, MyObj2, ..., MyObjN do
or nested expressions with , as in
with MyConverter do with MyOptionsDialog do with MyConverterExtension do ..
can cause a lot of conflict.
In defense of with instructions
I notice that there is almost a consensus (at least in this thread) that the statement with more evil than good. Although I am aware of a possible confusion and have fallen a couple of times, I cannot agree. Careful use of the with statement can make the code more attractive. And this reduces the risk of confusion due to "barfcode" .
For instance:
Comparison
var verdata: TVerInfo; verdata := GetFileVerNumbers(FileName); result := IntToStr(verdata.vMajor) + '.' + IntToStr(verdata.vMinor) + '.' + IntToStr(verdata.vRelease) + '.' + IntToStr(verdata.vBuild);
from
with GetFileVerNumbers(FileName) do result := IntToStr(vMajor) + '.' + IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' + IntToStr(vBuild);
There is absolutely no risk of confusion, and we not only store the variable temporaray in the latter case - it is also 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 examples of with statements, all simplifying the code.
In addition, as stated above, there is no risk of using with at all if you know what you are doing. But what if you want to use the with statement with MyObject in the example above: then inside the with Caption statement is MyObject.Caption . How do you change the title of a form? Plain!
with MyObject do begin Caption := 'This is the caption of MyObject.'; Self.Caption := 'This is the caption of Form1 (say).'; end;
Another place where it can be useful is to work with the result of a property or function, which requires a non-trivial amount of time to execute.
To work with the TClipper example above, suppose you have a list of TClipper objects with a slow method that returns a clipper for a specific TabSheet.
Ideally, you should only call this getter once, so you can use an explicit local variable or an implicit one using c .
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, any method will do, but in some cases, usually under difficult conditions a c may 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, it is a matter of personal taste. I usually use only one with a very narrow area and never nest them. Used in this way, they are a useful tool for reducing barfcode.