德尔福的"免费"做什么?

Agn*_*ian 4 delphi pascal

我在这里找到了以下代码片段:

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

只是很好奇,free声明/功能(在finally和之间end)做了什么?谷歌没有帮助.

And*_*and 23

代码

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

是简写

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

TClipper.Create创建一个类型的对象TClipper,并返回它,并且该with语句与大多数语言一样,允许您在不使用NameOfObject.MethodOrProperty语法的情况下访问此TClipper对象的方法和属性.

(一个更简单的例子:

MyPoint.X := 0;
MyPoint.Y := 0;
MyPoint.Z := 0;
MyPoint.IsSet := true;
Run Code Online (Sandbox Code Playgroud)

可以简化为

with MyPoint do
begin
  X := 0;
  Y := 0;
  Z := 0;
  IsSet := true;
end;
Run Code Online (Sandbox Code Playgroud)

)

但在您的情况下,您永远不需要将TClipper对象声明为变量,因为您创建它并可以通过with构造访问其方法和属性.

所以你的代码几乎是平等的

var
  Clipper: TClipper;

Clipper := TClipper.Create;
Clipper.AddPolygon(subject, ptSubject);
Clipper.AddPolygon(clip, ptClip);
Clipper.Execute(ctIntersection, solution);
Clipper.Free;
Run Code Online (Sandbox Code Playgroud)

第一行Clipper := TClipper.Create创建一个TClipper对象.以下三行与此对象一起使用,然后Clipper.Free销毁该对象,释放RAM,还可能释放对象使用的CPU时间和OS资源TClipper.

但上面的代码是不好的,因为如果一个错误occurrs(创建一个异常)内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;
Run Code Online (Sandbox Code Playgroud)

即使创建了异常,即使你在和之间调用,也保证运行finally和之间的代码.endExittryfinally

梅森所意味的是with,由于标识符冲突,有时构造可能是......大脑中的油漆.例如,考虑一下

MyObject.Caption := 'My test';
Run Code Online (Sandbox Code Playgroud)

如果你在with构造中写这个,即如果你写

with MyObect do
begin
  // A lot of code
  Caption := 'My test';
  // A lot of code
end;
Run Code Online (Sandbox Code Playgroud)

然后你可能会感到困惑.实际上,通常会Caption :=更改当前表单的标题,但现在,由于该with声明,它将改变MyObject的标题.

更糟糕的是,如果

MyObject.Title := 'My test';
Run Code Online (Sandbox Code Playgroud)

并且MyObject没有Caption属性,你忘了这个(并且认为属性被调用Caption),然后

MyObject.Caption := 'My test';
Run Code Online (Sandbox Code Playgroud)

甚至不会编译,而

with MyObect do
begin
  // A lot of code
  Caption := 'My test';
  // A lot of code
end;
Run Code Online (Sandbox Code Playgroud)

将编译得很好,但它不会做你期望的.

另外,构造如

with MyObj1, MyObj2, ..., MyObjN do
Run Code Online (Sandbox Code Playgroud)

或嵌套with语句,如

with MyConverter do
  with MyOptionsDialog do
    with MyConverterExtension do
      ..
Run Code Online (Sandbox Code Playgroud)

会产生很多冲突.

捍卫With声明

我注意到,这个with陈述几乎是一致的(至少在这个帖子中),这个陈述比邪恶更好.虽然我知道可能存在混淆,并且已经堕落了几次,但我不能同意.仔细使用该with语句可以使代码看起来更漂亮.这减少了由于"barfcode"造成混淆的风险.

例如:

相比

var
  verdata: TVerInfo;

verdata := GetFileVerNumbers(FileName);
result := IntToStr(verdata.vMajor) + '.' + IntToStr(verdata.vMinor) + '.' + IntToStr(verdata.vRelease) + '.' + IntToStr(verdata.vBuild);
Run Code Online (Sandbox Code Playgroud)

with GetFileVerNumbers(FileName) do
  result := IntToStr(vMajor) + '.' + IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' + IntToStr(vBuild);
Run Code Online (Sandbox Code Playgroud)

绝对没有混淆的风险,我们不仅在最后一种情况下保存了一个临时变量 - 它也更具可读性.

或者这个非常非常标准的代码:

with TAboutDlg.Create(self) do
  try
    ShowModal;
  finally
    Free;
  end;
Run Code Online (Sandbox Code Playgroud)

究竟混淆的风险在哪里?从我自己的代码中,我可以提供数百个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;
Run Code Online (Sandbox Code Playgroud)

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

要使用上面的TClipper示例,假设您有一个TClipper对象列表,其中包含一个返回特定TabSheet 的限幅器的慢速方法.

理想情况下,你应该只把这种吸气一次,所以你可以使用一个明确的局部变量,或者使用隐含一个.

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

要么

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

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

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

要么

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

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

  • 在IDE可以找出"with"所指的内容之前,我将继续避免它. (5认同)
  • 当然,您的意思是*调试器*,而不是IDE.IDE本身对with的含义没有任何问题.IDE的编译器部分可以非常准确地编译"with"代码,Code Insight可以正确地解析引用.只有调试器才能做到正确. (5认同)

Mas*_*ler 15

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

if self <> nil then
  self.Destroy;
Run Code Online (Sandbox Code Playgroud)

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

这是你不应该使用的一个很好的例子with.它往往使代码更难阅读.

  • 这是一个*非常方便和标准的结构.这样,如果您只是想在某处使用一些方法,则不需要使用大量的TOpenDialog,TMyConverter,TMyCustomDialog,TMySoundLibrary变量等来启动过程/单元. (7认同)
  • +1我在我的Delphi Anti-Patterns列表中添加了`with` :) (6认同)
  • @mjustin:我理解可能的(不可预见的)错误和对with语句的异议,但是有一些例子它实际上提高了代码的可读性并防止了非常冗长的变量语句(如果我们可以在各处声明变量就好了C虽然语言纯粹主义者可能也不喜欢这样. (2认同)
  • 代码非常容易阅读.海报不知道"免费"是做什么的,也不知道"和"是如何工作的.消除"with"的使用仅解决了该理解差距的一个HALF. (2认同)

Joh*_*lan 7

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