Any resources / Tutorials on using nested C statements in Delphi?

I am trying to figure out how to use instructions in delphi correctly.

In general, it seems pretty simple to do simple things, but I'm interested in finding good code examples and / or tutorials on using nested statements. For instance.

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

What I'm not sure is the priority order when used with operators in this way.

Any advice is appreciated.

+6
source share
5 answers

The best advice I can give you:

Use is not always.

If you enjoy using the “c,” go and lie until the sensation passes.

If you feel that you are using the nested ones, wrap your hand with a hammer until the desire disappears.

'with' is just an error awaiting appearance. Changing the classes used with it can change the meaning of your code. This creates inaccurate semantics, and it is always bad.

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

'C' should be avoided.

+20
source

An excerpt from online help reads:

When several objects or records appear after an object, the entire operator is considered as a series of nested operators. 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 name or method name in the expression is interpreted, if possible, as a member of objn ; otherwise interpreted, if possible, as a member of objn1 ; etc. The same rule applies to the interpretation of the objects themselves, so for instance, if objn is a member of both obj1 and obj2 , it is interpreted as obj2.objn .

+7
source

Now that there are enough opinions, I will try to fully answer your question, although this previous answer answers the syntax part of your question pretty well. His link to the documentation explains the order of priority, as well as other interesting rules. View this as a required reading.

As you already understood, the only problem with (unsested) with is that instead of accessing a specific instance member, you can access the Self member (or instance from one nested level) with the same name, which you obviously did not intend:

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

Run and be surprised that the form title has changed. Of course, TControl declares a Caption property, but it is protected, so Caption in this code refers to the form's form. But the following example does not guarantee the setting of the button header:

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

... because maybe Button1 declared as some kind of exotic button type that has a signature, but the property name can be Title , Beschriftung or Legende .

These are simple examples that are easy to fix and require little debugging. But consider non-visual elements of records and objects in memory: these errors are very difficult to debug, because: where to look? Of course, with little luck, Self does not have such a member if the compiler warns:

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

... but do not expect only errors that are caught by the compiler.

These mistakes are made very easily. For me, you and any other (experienced) developer. Especially when you are working or working with code that is under development or is being reorganized. And, obviously, with nested s debugging problems from these errors accumulate to exponential proportions.

Now, unlike the rest of us, I often use with , but only for fixed types of libraries whose members will never be renamed. For example: TRect, TPoint, TMessage, TGridRect, TControl, TCanvas, etc. .... Ie on almost all types of RTL records, as well as on some VCL classes, but almost never in my library types. If you really want to use with , I suggest the following:

  • Safe example:

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

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

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

Edit:

I became a member of no-with-camp.

My examples above suggest danger with with only in the upstream direction, for example, mixing Child.Caption with Self.Caption. But recently, when porting code from D7 to XE2, I ran into a problem in the downstream direction. In fact, using the “safe example” from above is pretty confident:

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

The intent of Width will be Self , but since XE2 TRect also has a Width member, I had to rewrite the code to:

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

Conclusion: there really is no safe use with .

+7
source

Delphi with problematic - this "semantic inaccuracy" can really bite you from behind. Nested with ... OMG ... Waiting to happen. Never need an embedded version for 13 years of Delphi. And I will avoid for the next years.

EDIT: In other discussions about with some have indicated that the new syntax would be nice. The starting point is Vb.Net with :

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

I don't like it (a question of my taste) a bit, but it's much better than Delphi's. The alternative syntax suggestion I saw in these discussions is as follows:

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

I hate to even talk about it, but it’s not difficult to understand how the rules of the region work. The last victory. Is always.

But people have this problem, which in psychology is called Chunk Theory. When you are offered seven things that you need to track in your mind, one drops out because your brain has about 6 local registers to hold things. Imagine a simple Foo id:

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

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

So, let's see how simple rules, such as a domain, become too complex to follow after a while. Imagine that in some delphi applications we work in the following areas:

  • The unit of measure that is in your use clauses.
  • Your own block implementation area (Internal Elements)
  • Class (identifiers inside your current class if you write a method)
  • Local area of ​​procedures or methods (var section in a procedure or function).
  • The first with the statement in the example above.
  • The second with the expression in the example above.

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

Now the problem is not that the WITH statement is unclear, it is that people can easily get lost when you have 6 layers of lexical coverage, as mentioned above, and requires that you not only know about all the places where Foo is defined, because if you think that the internal With statement has something called Foo , and you are not getting pretty nasty and hard to find an error in your code. So, we have very smart, very capable people, such as Nick, saying very reasonable things like "never use." And I agree 99% with Nick.

I also think that you could say that the adversary is not with this, Delphi developers tend to just continue to grow their applications iteratively in the RAD style until they become monsters. A unit that uses 200 other units is a mess, each of which uses 200 other units, will lead to even greater grief, even than the rampant abuse of WITH statements.

WITH is not the whole problem in bad code, it is simply the most common bee in the hood of a Delphi developer. Maybe it attracts more attention than overly large offers, but throughout my career, addictions - hell and huge uses - things have done 100 times more to make life harder than WITH . Therefore, I believe that WITH considered harmful recycled, and other things that should be considered more are being considered.

A reasonable alternative to using c is to use a variable name with a single letter (I know, agree and disagree) and avoid any 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; 

This is better, many will 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 have been bitten WITH too much to defend its unlimited use. But I also think that there are times when this is actually better than using local variables. This does not require you to declare a local variable or define the type of expression. If delphi has a type inference (the auto keyword in C ++), then I would say that we could easily do without WITH completely, but be that as it may, this is a kind of way to avoid the static creation of implementation-dependent types, and also the ability to create readable sub-areas, sometimes it can 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 this issue are changing, and, like other programming topics, they tend to turn into a discussion on a bike scam or, even worse, a holy war.

My suggested rules:

  • Avoid WITH unless this makes your code better. In most places that you think you need, you do not use a local variable.

  • Always avoid multi-level WITH statements.

+2
source

Source: https://habr.com/ru/post/906174/


All Articles