“免费”是什么意思?在德尔福做什么?

发布于 2024-09-07 17:05:52 字数 400 浏览 3 评论 0原文

我在此处找到了以下代码片段:

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

只是好奇,free<有什么作用? /code> 语句/函数(在 finallyend 之间)在这里做什么?谷歌没有提供帮助。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

写给空气的情书 2024-09-14 17:06:03

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

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 :)

小兔几 2024-09-14 17:06:01

任何动态创建的对象都必须调用free来释放对象创建时使用后分配的内存。 TClipper 对象是一个桌面内容创建、捕获和管理工具。所以它是某种 Delphi 与 Clipper 的连接对象。 create(对象创建)在try finaly end;语句中处理,这意味着,如果与 Clipper 的连接不成功,则对象 TClipper 将不会被创建,也不能被创建。在 try Finaly end; 语句之后释放。

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.

小忆控 2024-09-14 17:06:00

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

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....

落花浅忆 2024-09-14 17:05:59

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

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

只等公子 2024-09-14 17:05:57

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

if self <> nil then
  self.Destroy;

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

这是一个很好的例子,说明了为什么不应该使用 with。它往往会使代码更难阅读。

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.

对不⑦ 2024-09-14 17:05:56

该代码

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 之间的代码保证运行,即使创建了异常,并且即使您在 tryfinally 之间调用 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;

只会编译很好,但它不会达到您的期望。

此外,像 in 那样构造

with MyObj1, MyObj2, ..., MyObjN do

或嵌套 with 语句

with MyConverter do
  with MyOptionsDialog do
    with MyConverterExtension do
      ..

可能会产生很多冲突。

为了捍卫 With 语句,

我注意到几乎有一个共识(至少在这个线程中):with 语句弊大于利。尽管我意识到潜在的混乱,并且已经陷入其中几次,但我不能同意。仔细使用with语句可以使代码看起来更漂亮。这减少了由于“barfcode”而造成混乱的风险。

例如:

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);

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

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

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;

OR

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,并且从不嵌套它们。通过这种方式使用,它们是减少条形码的有用工具。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文