有关使用嵌套“With”的任何资源/教程Delphi 中的语句?

发布于 2024-12-27 12:18:27 字数 242 浏览 4 评论 0原文

我正在尝试正确处理 delphi 中的 with 语句。

总的来说,做简单的事情似乎相当简单,但我有兴趣找到一些关于使用嵌套 with 语句的好的代码示例和/或教程。 EG

with object1, object2, etc... do 
  begin
  statements
  end;

我不确定以这种方式使用 with 语句时的优先顺序。

任何建议表示赞赏。

I am trying to come to grips with using with statements in delphi properly.

Overall it seems fairly simple to do simple things with but I am interested in finding some good code examples and/or tutorials on using nested with statements. E.G.

with object1, object2, etc... do 
  begin
  statements
  end;

What I am unsure of is the order of precedence when using with statements in such a manner.

Any advice is appreciated.

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

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

发布评论

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

评论(5

萌能量女王 2025-01-03 12:18:27

我能给你的最好建议是:

永远不要使用“with”。

如果你想使用“with”,就躺下来直到这种感觉过去。

如果您想使用嵌套,请用锤子敲打您的手,直到这种愿望消失为止。

“with”只是一个等待发生的错误。改变与它一起使用的类可以改变代码的含义。它会产生不精确的语义,这总是不好的。

节省击键次数从来都不是使用“with”的好理由。现在多敲几下键盘,以后就能省去很多麻烦。

应避免使用“与”。

The best advice I can give you is:

Do not use with ever.

If you feel like using 'with', go and lie down until the feeling passes.

If you feel like using a nested with, pound your hand with a hammer until the desire goes away.

'with' is just a bug waiting to happen. Altering classes used with it can alter the meaning of your code. It creates imprecise semantics, and that is always bad.

Saving keystrokes is never a good reason to use 'with'. A few more keystrokes now will save you a lot of pain later.

'With' should be shunned.

白色秋天 2025-01-03 12:18:27

在线参考的摘录指出:

当with后面出现多个对象或记录时,整个
语句被视为一系列嵌套的 with 语句。因此:

with obj1, obj2, ..., objn do 语句

相当于:

 与 obj1 一起执行
  与 obj2 一起做
    ...
    与 objn 一起做
      // 陈述

在这种情况下,语句中的每个变量引用或方法名称是
如果可能的话,解释为 objn 的成员;否则就是
如果可能的话,解释为 objn1 的成员;等等。相同
规则适用于解释对象本身,因此,对于
例如,如果 objn 是 obj1 和 obj2 的成员,则它会被解释
作为obj2.objn

An excerpt from the online reference states:

When multiple objects or records appear after with, the entire
statement is treated like a series of nested with statements. Thus:

with obj1, obj2, ..., objn do statement

is equivalent to:

 with obj1 do
  with obj2 do
    ...
    with objn do
      // statement

In this case, each variable reference or method name in statement is
interpreted, if possible, as a member of objn; otherwise it is
interpreted, if possible, as a member of objn1; and so forth. The same
rule applies to interpreting the objs themselves, so that, for
instance, if objn is a member of both obj1 and obj2, it is interpreted
as obj2.objn.

人事已非 2025-01-03 12:18:27

既然已经给出了足够的意见,我将尝试完全回答您的问题,尽管这个先前的答案回答了语法部分你的问题已经很好了。它对文档的引用解释了优先级顺序,以及其他有趣的内容关于与的规则。将其视为必读。

正如您可能已经理解的,(未嵌套的)with唯一问题是,您可以寻址 的成员,而不是寻址特定的实例成员。 Self(或来自一个嵌套级别的实例)具有相同的名称,显然您不打算这样做:

procedure TForm1.SomeRoutine;
var
  Button: TControl;
begin
  Button := Button1;
  with Button do
    Caption := 'Press here';
end;

运行,并惊讶于表单的标题已更改。当然,TControl 声明了一个 Caption 属性,但该属性是受保护的,因此此代码中的 Caption 引用了表单的属性。但下一个示例也不能保证设置按钮的标题:

procedure TForm1.SomeRoutine;
begin
  with Button1 do
    Caption := 'Press here';
end;

...因为也许 Button1 被声明为某种异国情调的按钮类型,它一个标题,但是属性名称可以是 TitleBeschriftungLegende

这些都是简单的示例,很容易修复并且几乎不需要调试。但考虑内存中记录和对象的非可视成员:这些错误很难调试,因为:去哪里看?当然,如果运气好的话,Self 没有这样的成员,以防编译器发出警告:

procedure TForm1.SomeRoutine;
begin
  with Button1 do
    Cpation := 'Press here';
end;

...但不要指望只会出现编译器捕获的错误。

这类错误非常很容易犯。由我,由你,以及由任何其他(有经验的)开发人员。特别是当您处理或使用正在构建或正在重构的代码时。显然,使用嵌套的 with 时,这些错误带来的调试问题会呈指数级增长。

现在,与我们这里的其他人不同,我经常使用 with,但仅限于固定的库类型,这些类型的成员永远不会被重命名。例如:TRect、TPoint、TMessage、TGridRect、TControl、TCanvas 等...即几乎所有 RTL 记录类型以及一些 VCL 类,但几乎从未在我自己的库类型上。如果您确实想使用 with,我建议如下:

  • 安全示例:

    与 ARect 一起做
      ExcludeClipRect(DC, 左, 上, 右, 下); //TRect 总会有这些
    
  • 棘手示例:

    带查询,参数做
    开始
      连接 := A连接;
      ParamValues['ID'] := ID; //TParameters 有一些与 dataset 相同的命名成员
      打开;
    结尾;
    
  • 糟糕的例子:

    用 TMyLabel 做
      颜色:= cl黄色; //我的标签将来可能会变得透明
    

编辑:

我已经成为 no-with-camp 的成员。

我上面的例子假设 with 仅在向上方向存在危险,例如将 Child.Caption 与 Self.Caption 混合在一起。但最近,当我将一些代码从D7移植到XE2时,我遇到了向下的麻烦。实际上,使用上面的“安全示例”,确实如此:

  with GripRect[I] do
  begin
    ...
    Left := Width - W[I];

意图将 Width 设为 Self 的宽度,但由于 XE2 的 TRect 也有一个Width 成员,我不得不将代码重写为:

    Left := Self.Width - W[I];

结论:确实没有安全使用 with

Now that there are enough opinions given, I will try to fully answer your question, though this previous answer answers the syntax part of your question pretty well already. Its reference to the documentation explains the order of precedence, as well as other interesting rules about with. Consider it as a mandatory read.

As you may have understood already, the only problem with (an unnested) with is that instead of addressing a specific instance member, you could be addressing a member of Self (or of an instance from one nested level up) with the same name, which you obviously did not intend to:

procedure TForm1.SomeRoutine;
var
  Button: TControl;
begin
  Button := Button1;
  with Button do
    Caption := 'Press here';
end;

Run, and be surprised the form's caption is changed. Sure, TControl declares a Caption property, but that one is protected, thus Caption in this code refers to that of the form. But also the next example does not guarantee setting the caption of the button:

procedure TForm1.SomeRoutine;
begin
  with Button1 do
    Caption := 'Press here';
end;

... because maybe Button1 is declared as some exotic button type which has a caption, but the property name could be Title, Beschriftung, or Legende.

These are simple examples which are easily fixed and almost need no debugging. But consider non-visual members of in-memory records and objects: those mistakes are very hard to debug, because: where to look? Sure, with a little luck, Self does not have such member, in case the compiler will warn:

procedure TForm1.SomeRoutine;
begin
  with Button1 do
    Cpation := 'Press here';
end;

... but do not expect to only make errors which are caught by the compiler.

These kind of mistakes are made very easily. By me, by you, and by any other (experienced) developer. Especially when you work on or work with code which is under construction or is being refactored. And obviously, with nested withs, the debugging problems from these mistakes are piling up to exponential proportions.

Now, unlike the rest of us here, I use with quite often, but only on fixed library types which' members are never to become renamed. E.g.: TRect, TPoint, TMessage, TGridRect, TControl, TCanvas, etc... I.e. on almost all RTL record types as well as some VCL classes, but almost never on my own library types. If you really wánt to use with, I suggest the following:

  • Safe example:

    with ARect do
      ExcludeClipRect(DC, Left, Top, Right, Bottom);  //TRect will always have these
    
  • Tricky example:

    with Query, Parameters do
    begin
      Connection := AConnection;
      ParamValues['ID'] := ID; //TParameters has a few same named members as dataset
      Open;
    end;
    
  • Bad example:

    with TMyLabel do
      Color := clYellow; //My label may become transparent in future
    

Edit:

I have become a member of the no-with-camp.

My examples above assume danger with with only in upward direction, like mixing up Child.Caption with Self.Caption. But recently, when porting some code from D7 to XE2, I experienced trouble in downward direction. Actually, using the 'safe example' from above, sure enough:

  with GripRect[I] do
  begin
    ...
    Left := Width - W[I];

Intending Width to be that of Self, but since XE2's TRect has also a Width member, I had to rewrite the code to:

    Left := Self.Width - W[I];

Conclusion: There really is no safe usuage of with.

年华零落成诗 2025-01-03 12:18:27

Delphi 的 with 是有问题的——它的“不精确的语义”确实会给你带来麻烦。
嵌套... 天啊... 一场灾难即将发生。从来不需要嵌套版本
德尔福13年。我将在接下来的几年里避免这样做。

编辑:
在其他关于 with 的讨论中,一些人指出新的语法会很好。一个起点
is Vb.Net with

With Something.Somewhere
  .over.the.rainbow = True
End With

我有点不喜欢(这是我的品味问题),但它比 Delphi 的好得多。我在这些讨论中看到的替代语法的建议是:

With SS:Something.Somewhere.over do
  SS.the.rainbow = true;
  SS.the.Storm = false;
end;

Delphi's with is problematic - it's "imprecise semantics" really can bite you on the rear.
Nested with... OMG... Is a disaster waiting to happen. Never needed the nested version on
13 years of Delphi. And I'll avoid for the next years.

EDIT:
In other discusions about with, some pointed that a new syntax would be nice. An starting point
is Vb.Net with:

With Something.Somewhere
  .over.the.rainbow = True
End With

I dislike (a matter of taste mine) a little, but it's much better than Delphi's. An suggestion of alternative syntax which I saw on those discussions is:

With SS:Something.Somewhere.over do
  SS.the.rainbow = true;
  SS.the.Storm = false;
end;
淡淡绿茶香 2025-01-03 12:18:27

我什至不想这么说,但弄清楚范围规则如何工作并不难。最后范围获胜。总是。

但人类有这个问题,在心理学上称为块理论。当你的脑海中出现七件需要记住的事情时,其中一件会掉下来,因为你的大脑大约有 6 个本地寄存器来保存事情。想象一下简单的标识符 Foo 出现在这里:

   with EXPRESSION1 do begin
     with EXPRESSION2 do begin
               Foo;
     end;
   end;

上面是对于与 EXPRESSION1、EXPRESSION2 相同的所有意图,请开始...。

因此,让我们看看像范围这样的简单规则如何在一段时间后变得过于复杂而难以遵循。假设我们正在使用的某个 delphi 应用程序具有以下级别的作用域:

  • use 子句中每个单元的单元作用域。
  • 您自己的单元的实现部分范围(单元内部)
  • 类范围(如果您正在编写方法,则为当前类中的标识符)
  • 本地过程或方法范围(过程或函数中的 var 部分)。
  • 上面示例中的第一个 with 语句。
  • 上面示例中的第二个 with 语句。

更新 David 向我指出,我应该提到,我们实际上有 5+x 个范围,而不是上面的 6 个“范围”,其中 x 是您的使用的长度 条款。

现在,问题不是WITH语句不清楚,而是当你有6层词法作用域时,人类很容易迷失方向,如上所述,并且要求你不仅要知道的所有地方Foo 已定义,因为如果您认为最里面的 With 语句定义了名为 Foo 的内容,那么您的代码中就会出现相当令人讨厌且难以发现的错误。因此,我们有像尼克这样非常聪明、非常有能力的人,他们会说“永远不要与”一起使用等非常明智的事情。我 99% 同意 Nick 的观点。

我还认为你可以说敌人不存在,这是 Delphi 开发人员必须以“RAD 风格”迭代地不断开发他们的应用程序的趋势,直到它们变成怪物。一个使用 200 个其他单元的单元是一团糟,每个单元都使用 200 个其他单元,这甚至会比肆意滥用 WITH 语句给您带来更多的痛苦。

WITH 并不是糟糕代码中的全部问题,它只是 Delphi 开发人员帽子中最常见的蜜蜂。也许它比过大的uses-clauses更受关注,但在我的整个职业生涯中,依赖地狱和巨大的uses-clauses使生活变得比WITH多100倍。所以我认为WITH被认为是有害的做得太过了,而其他应该更多考虑的事情却没有被考虑。

使用 with 的一个合理替代方案是使用单字母变量名(我知道,继续并不同意),并避免所有范围歧义:

procedure NoWithPlease;
var
   a:TSomething;
begin
   a :=  Some.Long.Expression[x].That.You.Dont.Want.To.Repeat.Six.Times;
   a.Foo := 'test';
   a.Bar := 'test2'
end;

很多人会说,这比这个更好:

procedure WithPleasure;
begin
   with Some.Long.Expression[x].That.You.Dont.Want.To.Repeat.Six.Times do begin
     Foo := 'test';
     Bar := 'test2'
   end;
end;

现在我被 < 咬住了code>WITH 太多次,提倡不受限制地使用。但我也认为有时它实际上比执行局部变量更好。它不需要您声明局部变量或确定表达式的类型。如果delphi有类型推断(C++中的auto关键字),那么我想说,我们可以轻松地完全不用WITH,但是因为它是的,这是一种避免静态创建对实现类型的依赖的方法,并且除了创建可读子范围的能力之外,它有时会使代码更易于阅读,有时会使代码变得更糟,尤其是当存在超过一级WITH 语句。

然而,对此事的看法各不相同,并且像其他编程主题一样,往往会变成自行车棚的讨论,或更糟糕的是,变成一场圣战。

我建议的规则:

  1. 避免WITH,除非它使你的代码更好。大多数您认为需要 with 的地方,实际上并不需要,而是使用局部变量。

  2. 始终避免多级 WITH 语句。

I hate to even say this, but it's not exactly hard to figure out how scope rules work. Last scope in wins. Always.

But human beings have this problem, which in psychology is called Chunk Theory. When you are presented with seven things to keep track of in your mind, one falls out, because your brain has about 6 local registers to keep things in. Imagine the simple identifier Foo appears here:

   with EXPRESSION1 do begin
     with EXPRESSION2 do begin
               Foo;
     end;
   end;

The above is for all intents identical to with EXPRESSION1, EXPRESSION2 do begin....

So let's see how simple rules like scope get too complicated to follow after a while. Let's imagine that we have the following levels of scope in some delphi application we're working in:

  • The unit scope of every unit that is in your use clauses.
  • Your own unit's implementation section scope (Unit-internals)
  • The class scope (identifiers inside your current class if you are writing a method)
  • The local procedure or method scope (var section in a procedure or function).
  • The first with statement in the sample above.
  • The second with statement in the sample above.

Update David has pointed out to me that I should mention that instead of 6 "scopes" above, we really have 5+x scopes, where x is the length of both your uses clauses.

Now, the problem isn't the WITH statement being unclear, it's that human beings can easily get lost when you have 6 layers of lexical scoping going on, as above, and requires you not only to be aware of all places where Foo is defined, because if you thought that the innermost With statement has something named Foo defined, and it doesn't you get a rather nasty and hard to find bug in your code. And so, we have very smart, very capable people like Nick, saying very sensible things like "never use with". And I agree about 99% with Nick.

I also think that you could say that the enemy is not with, it's the tendency Delphi developers have to just keep growing their applications iteratively in a "RAD style" until they have become monstrosities. A unit which uses 200 other units is a mess, each of which uses 200 other units, is going to cause you more grief even than rampant abuse of WITH statements.

WITH is not the entire problem in bad code, it's just the most common bee in a Delphi developer's bonnet. Maybe it gets more attention than over-large uses-clauses, but over my entire career, dependency-hell and enormous-uses-clauses have done 100 times more to make life hard, than WITH. So I think that WITH considered harmful is over-done, and other things which ought to be considered more, are under considered.

A reasonable alternative to using with, is to use a single letter variable name (I know, go ahead and disagree), and avoid all scope ambiguity:

procedure NoWithPlease;
var
   a:TSomething;
begin
   a :=  Some.Long.Expression[x].That.You.Dont.Want.To.Repeat.Six.Times;
   a.Foo := 'test';
   a.Bar := 'test2'
end;

That is better, many would say, than this:

procedure WithPleasure;
begin
   with Some.Long.Expression[x].That.You.Dont.Want.To.Repeat.Six.Times do begin
     Foo := 'test';
     Bar := 'test2'
   end;
end;

Now I've been bitten by WITH too many times to advocate its unrestricted use. But I also think there are times when it's actually better than doing the local variable stuff. It doesn't require that you declare a local variable, or determine the type of the expression. If delphi had type inference (the auto keyword in C++), then I would say, we could easily do without WITH completely, but as it is, it is kind of a way to avoid statically creating a dependency on implementation types, and along with the ability to create readable sub-scopes, it can sometimes make your code easier to read, and sometimes make it worse, especially when there is more than one level of WITH statements.

However, opinions on that matter vary, and like other programming topics, tends to turn into a bike-shed discussion, or worse yet, a holy war.

My suggested rules:

  1. Avoid WITH unless it makes your code better. Most places that you think you need a with, you don't, use a local variable instead.

  2. Always avoid multi-level WITH statements.

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