德尔福的“免费”做什么?

时间:2010-06-29 16:43:36

标签: delphi pascal

我找到了以下代码段here

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

好奇,free语句/功能(finallyend之间)在这里做了什么?谷歌没有帮助。

6 个答案:

答案 0 :(得分:23)

代码

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语句(与大多数语言一样)允许您访问此TClipper对象的方法和属性不使用NameOfObject.MethodOrProperty语法。

(一个更简单的例子:

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销毁该对象,释放RAM,还可能释放TClipper对象使用的CPU时间和操作系统资源。

但是上面的代码并不好,因为如果在AddPolygonExecute内发生错误(创建异常),那么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;

finallyend之间的代码保证会运行,即使您创建了例外,即使您在Exit和{{1之间调用try也是如此}}

Mason的意思是,由于标识符冲突,有时finally构造可能是......大脑中的一个绘画。例如,考虑

with

如果你在MyObject.Caption := 'My test'; 结构中写下这个,即你写的是

with
那么你可能会感到困惑。实际上,通常with MyObect do begin // A lot of code Caption := 'My test'; // A lot of code end; 更改当前表单的标题,但现在,由于Caption :=语句,它将改变MyObject的标题。

更糟糕的是,如果

with

并且MyObject没有MyObject.Title := 'My test'; 属性,你忘记了这一点(并认为该属性被称为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语句可以使代码看起来更漂亮。这减少了"barfcode"导致混淆的风险。

例如:

比较

with

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

另一个可能有用的地方是使用属性或函数结果时需要花费很多时间来执行。

要使用上面的TClipper示例,假设您有一个带有 slow 方法的TClipper对象列表,该方法返回特定TabSheet的限幅器。

理想情况下,您应该只调用此getter一次,因此您可以使用显式局部变量,也可以使用带的隐式变量。

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

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

在这样的情况下,任何一种方法都可以,但在某些情况下,通常在复杂的条件下,a可以更清晰。

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

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

最终是个人品味的问题。我通常只使用带有的,范围非常窄,并且永远不会嵌套它们。以这种方式使用它们是减少 barfcode 的有用工具。

答案 1 :(得分:15)

这是对TObject.Free的调用,基本上定义为:

if self <> nil then
  self.Destroy;

它正在with语句中创建的未命名TClipper对象上执行。

这是您不应该使用with的一个很好的例子。它往往会使代码难以阅读。

答案 2 :(得分:7)

Free调用对象的析构函数,释放对象实例占用的内存。

答案 3 :(得分:3)

我对Delphi一无所知,但我认为它正在释放TClipper使用的资源,就像C#中的using语句一样。这只是猜测......

答案 4 :(得分:0)

任何dinamicly创建的对象必须在使用后调用 free 释放对象创建的alocated内存。 TClipper对象是桌面内容创建,捕获和管理工具。所以它是某种带有Clipper的Delphi连接对象。 创建(对象创建)在尝试finaly end; 语句中处理是什么意思,如果与Clipper的连接不成功,则不会创建对象TClipper,也不能在尝试最后结束之后释放; 声明。

答案 5 :(得分:0)

如果“有”像某些海报所暗示的那样邪恶,他们可以解释一下 1.为什么Borland创造了这种语言结构,和 2.为什么他们(Borland / Embarcadero / CodeGear)在他们自己的代码中广泛使用它?
虽然我当然明白一些Delphi程序员不喜欢“with”,虽然承认有些用户滥用它,但我认为说“你不应该使用它”是愚蠢的。 angusj - 违规代码的作者:)