我在这里找到了以下代码片段:
with TClipper.Create do
try
AddPolygon(subject, ptSubject);
AddPolygon(clip, ptClip);
Execute(ctIntersection, solution);
finally
free;
end
我很好奇,在 finally
和 end
之间的 free
语句/函数在这里的作用是什么?Google 没有帮助。
我在这里找到了以下代码片段:
with TClipper.Create do
try
AddPolygon(subject, ptSubject);
AddPolygon(clip, ptClip);
Execute(ctIntersection, solution);
finally
free;
end
我很好奇,在 finally
和 end
之间的 free
语句/函数在这里的作用是什么?Google 没有帮助。
代码
with TClipper.Create do
try
AddPolygon(subject, ptSubject);
AddPolygon(clip, ptClip);
Execute(ctIntersection, solution);
finally
free;
end
是...的简写
with TClipper.Create do
begin
try
AddPolygon(subject, ptSubject);
AddPolygon(clip, ptClip);
Execute(ctIntersection, solution);
finally
free;
end;
end;
TClipper.Create
创建了一个类型为 TClipper
的对象,并返回该对象及其方法和属性。使用 with
语句,可以在不使用 NameOfObject.MethodOrProperty
语法的情况下访问此 TClipper 对象的方法和属性。这与大多数编程语言中的使用方式相同。
(一个更简单的例子:
MyPoint.X := 0;
MyPoint.Y := 0;
MyPoint.Z := 0;
MyPoint.IsSet := true;
with MyPoint do
begin
X := 0;
Y := 0;
Z := 0;
IsSet := true;
end;
)
但在您的情况下,您永远不需要声明TClipper对象作为变量,因为您创建它并可以通过with
结构访问其方法和属性。
因此,您的代码几乎相当于
var
Clipper: TClipper;
Clipper := TClipper.Create;
Clipper.AddPolygon(subject, ptSubject);
Clipper.AddPolygon(clip, ptClip);
Clipper.Execute(ctIntersection, solution);
Clipper.Free;
第一行,Clipper := TClipper.Create
,创建了一个TClipper
对象。以下三行操作这个对象,然后Clipper.Free
销毁了这个对象,释放了被TClipper
对象使用的RAM和可能的CPU时间和OS资源。
但是上述代码不好,因为如果在AddPolygon
或Execute
中发生错误(创建异常),那么Clipper.Free
将永远不会被调用,导致内存泄漏。为了防止这种情况,Delphi使用try...finally...end
结构:
Clipper := TClipper.Create;
try
Clipper.AddPolygon(subject, ptSubject);
Clipper.AddPolygon(clip, ptClip);
Clipper.Execute(ctIntersection, solution);
finally
Clipper.Free;
end;
< p > finally
和 end
之间的代码保证会执行,即使出现异常,甚至在 try
和 finally
之间调用 Exit
。
Mason 的意思是,有时候 with
结构可能会引起头疼,因为会出现标识符冲突。例如,请考虑:
MyObject.Caption := 'My test';
with
结构中编写此内容,即如果你编写with MyObect do
begin
// A lot of code
Caption := 'My test';
// A lot of code
end;
如果你不熟悉编程,可能会感到困惑。实际上,大多数情况下Caption :=
会更改当前窗体的标题,但是现在由于with
语句,它将更改MyObject的标题。
更糟糕的是,如果
MyObject.Title := 'My test';
如果MyObject没有Caption
属性,而你忘记了这一点(认为该属性被称为Caption
),那么
MyObject.Caption := 'My test';
无法编译,而
with MyObect do
begin
// A lot of code
Caption := 'My test';
// A lot of code
end;
我会编译,但是它不会按照你的期望工作。
此外,类似以下结构的代码
with MyObj1, MyObj2, ..., MyObjN do
或者像以下这样使用嵌套的with
语句:
with MyConverter do
with MyOptionsDialog do
with MyConverterExtension do
..
可以产生许多冲突。
With
语句辩护我注意到几乎有一个共识(至少在这个帖子中),即with
语句比好处更多。虽然我意识到潜在的混淆,并且已经犯过几次错误,但我不能同意。仔细使用with
语句可以使代码看起来更漂亮。这降低了由于"barfcode"而导致的混淆风险。
例如:
对比
var
verdata: TVerInfo;
verdata := GetFileVerNumbers(FileName);
result := IntToStr(verdata.vMajor) + '.' + IntToStr(verdata.vMinor) + '.' + IntToStr(verdata.vRelease) + '.' + IntToStr(verdata.vBuild);
使用
with GetFileVerNumbers(FileName) do
result := IntToStr(vMajor) + '.' + IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' + IntToStr(vBuild);
我完全没有混淆的风险,而且在最后一种情况下我们不仅保存了一个临时变量,而且代码更易读。
或者说这个非常标准的代码:
with TAboutDlg.Create(self) do
try
ShowModal;
finally
Free;
end;
风险在哪里?从我的代码中,我可以提供数百个更多的with
语句示例,所有这些都简化了代码。
此外,正如上面所述,只要你知道你在做什么,使用with
就没有任何风险。但是,如果您想要在上面的示例中将with
语句与MyObject
一起使用:那么,在with
语句内部,Caption
等于MyObject.Caption
。那么如何更改窗体的标题呢? 简单!
with MyObject do
begin
Caption := 'This is the caption of MyObject.';
Self.Caption := 'This is the caption of Form1 (say).';
end;
在处理需要执行时间较长的属性或函数结果时,with语句可以派上用场。
以上面的TClipper示例为例,假设您有一个TClipper对象列表,其中包含一个slow方法,该方法返回特定TabSheet的剪辑器。
理想情况下,您应该只调用此getter一次,因此您可以使用显式局部变量或使用with的隐式变量。
var
Clipper : TClipper;
begin
Clipper := ClipList.GetClipperForTab(TabSheet);
Clipper.AddPolygon(subject, ptSubject);
Clipper.AddPolygon(clip, ptClip);
Clipper.Execute(ctIntersection, solution);
end;
或者
begin
with ClipList.GetClipperForTab(TabSheet)do
begin
AddPolygon(subject, ptSubject);
AddPolygon(clip, ptClip);
Execute(ctIntersection, solution);
end;
end;
在这种情况下,任何一种方法都可以,但在某些情况下,通常在复杂的条件语句中,使用 with 可能更清晰。
var
Clipper : TClipper;
begin
Clipper := ClipList.GetClipperForTab(TabSheet);
if (Clipper.X = 0) and (Clipper.Height = 0) and .... then
Clipper.AddPolygon(subject, ptSubject);
end;
或
begin
with ClipList.GetClipperForTab(TabSheet) do
if (X = 0) and (Height = 0) and .... then
AddPolygon(subject, ptSubject);
end;
with
的主要缺点是它会让我们把代码放到错误的地方。如果 with
语句中的代码被放置在为其提供别名的结构内部,那么可以实现相同的简洁性优势,并且调用者的代码看起来更加干净。 - Caleb Hattinghwith
使用情况,其中大部分是记录,将代码添加到记录本身是没有任何意义的。 - Andreas Rejbrandwith
语句是合理的,那么我认为让记录通过方法调用对自身进行操作可能更好。 - Caleb Hattingh这是对TObject.Free的调用,它基本上被定义为:
if self <> nil then
self.Destroy;
这是在with
语句中创建的未命名的TClipper对象上执行。
这是为什么你不应该使用with
的很好的例子。它往往使代码更难读懂。
Free函数调用对象的析构函数,并释放该对象所占用的内存。
任何动态创建的对象必须在使用后调用free以释放分配的内存。TClipper对象是一个桌面内容创建、捕获和管理工具。因此,它是一种带有Clipper的Delphi连接对象。 create(对象创建)在try finally end;语句中处理,这意味着如果与Clipper的连接不成功,则对象TClipper将不会被创建,并且不能在try finally end;语句之后释放。
self
作用域。您可以将所有方法视为包装在一个with self do
块中,一旦有了两个隐式作用域层,就可能遇到一些非常棘手的解析问题,引入错误并使代码更难以修复。 - Mason Wheeler