I was recently discussing against the use of the with statement in Delphi with a customer and I realized that local variables offer a very good way to get rid of with statements
As any Delphi developer knows, the with statements have been long in the Object Pascal language since the early Pascal days, but they have also been considered harmful by many, myself included. To explain once more why, I’ll use a classic piece of code highlighting the issues with scope:
What does this code? It creates a button at the position of the mouse click (or mouse down, to be more precise) on a form, setting the form at parent, using the button’s class name as caption and turning its color to red. Well, this is what one might assume the last line of code to do, but it is in fact badly incorrect, producing this output:
Given the TButton class has no Color property, the compiler assumes Color refers to the current object of the method, that is the form. With statements add an object in the direct scope, but offer no way to differentiate between an operation on the object part of the with or the self object of the current method. This is fairly dangerous in a complex application.
The “modern” alternative to do repeated operations on a temporary variable is to use an inline variable. The code is very similar, but by prefixing each operation on TButton with the variable itself, the developer makes it clear to the compiler what she or he wants to achieve:
In case one attempts to assign the Color to the button, the compiler catches it and shows an error, which is a significant advantage compared to changing the Color of some other object.
Now to the original request: Are you going to remove the with statement from the Object Pascal language in Delphi? This is most likely not going to happen any time soon, but there have been requests to introduce an optional warning, so that the compiler can indicate whether you are using with in your code.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition
Before introducing warnings or even removing
with
please add a working refactoring to replace it.This is a very good idea
+1
Indeed a good idea.
I love the idea of refactoring things in Delphi to make it less verbose. +1 for you 🙂
They also obscure references, as shown in the OP’s simplified example, sometimes leading to subtle, hard to diagnose bugs.
I see little reason to use them since local variables will accomplish the same task in nearly as terse fashion.
I don’t advocate for out and out REMOVING them from the language though, they’re still useful on those rare occasions where an anonymous instantiation is warranted, just be careful that the references are doing what you meant.
What about the implied with? If you use a unit (unsing the USES statement), everything in the unit is automatically withed. The same goes for the implied “with self” in methods.
Bashing the explicit with (which you can simply not use), but ignoring the implied withs seems somewhat inconsequential to me.
The issue is not whether to use with-statements or not. It is whether to force other to do things your way! Adding warnings that can be disabled is fine. Removing it from the language is not fine. You have already removed floating form design because you prefer to do it in edit windows. Stop forcing to people to do things your way. Don’t become like Microsoft, etc.!
Just an idea, refactor adding a “dot” in front so you can have easy autocomplete and the variable will be not necessary:
With TButton.Create(self)
.Parent:= self;
.left:= x;
More clear would be adding a begin end, in that way the scope should be strict to the object it references:
with TButton.Create(self)
begin
.Parent:= self;
.left:= x;
.color (autocomplete will not work here since color does not exist in this scope)
end;
Yes this is an alternative idea I like too. If we keep
with
then adding the dot notation like you suggest would help solve the problem of comprehension – unless of course someone usewith
on more than one object such aswith cat, do begin .. end;
You mean nested ‘with’? The use of ‘with’ is meant to make coding easier and also add readability in small peaces of code. If you use nested ‘with’ for several objects in a routine, it’s confusing and should be considered a bad practice.
I use with sparingly, but sometimes it is very useful and not at all dangerous. I like the suggestion of using a dot in front of the properties. That is readable and concise. This way we can have our cake and eat it too. A compiler warning would probably be useful too, but I would not like to see the with statement go away The with statement is useful, but like other sharp tools, children and dimwits should stay away from it.
Removing
with
would be a great step. It is one the biggest code smells I have ever seen. A warning added to the compiler would be great. Having said that, we should add this to the SonarQube plugin! 🙂the dot prefix can be very efficient
with TLabel.Create(Self), TEdit.Create(Self) do
begin
..Caption := ‘Label’; // double dot to access TLabel
.TextHint := ‘Type something’; // simple dot to access TEdit
end,
I’ve introduced this notation in FlashPascal to support with for a Variant, because the compiler cannot resolve the scope of symbols and need a tip to associate things.
it also can be source of confusion…like any code with bad identation (I don’t want to replace begin/end by a strict indentation anyway like Python) or a too long procedure…but it’s another problem.
Another nice feature could be to have a semantical highlighter, I mean for instance that a local var, a parameter, a member or a global var could have different colors so you can immediately see (unless you’re daltonian) the scope of the elements in your code.
A “matching” can also give tips, for instance, when you click on TextHint , TEdit could be highlighted.
so readability is not only a matter of syntax.
I’d vote too for a semantical highlighter and/or hover tips with more info on the scope of some identifier in the code
btw, with’s biggest issues are due to compiler and debugger bugs/inconcistencies, including between the implementation and debugging on different platforms. E.g. see this hard to debug issue, where compiler and debugger behave differently even on Windows:
https://quality.embarcadero.com/browse/RSP-37186
with has big potential for guiding compiler optimizations and is much cleaner than copy-pasting variable names, can avoid use of local variables that leak their instance to the following code if you don’t add extra verbose begin-end wrapper, eases refactoring (no local var renames at multiple places etc. BTW, one-letter variable names aren’t very good for code readability, esp. when code gets longer and more complex.
also, with could be enhanced to do functionality like “using” of C# or “with resources” of Java where you’d create an object on the with statement, you’d use its properties etc. in the code block (optionally) and it would be autoreleased upon closing the block
note I’ve already suggested sometime ago the dot syntax (think VB/VB.net was also using it), it is way more readable and easy to parse by preprocessors too. Could combine it with conditionals that would disable optionally the old with behaviour and keep the new one. Of course the dot syntax of a new with would be made to never resolve outside the with statement. That’s the main readability and programmer mistake’s issue with with, that you don’t know which symbols resolve to with and which may resolve further outside due to some mispelling of a field etc. (apart from the compiler/debugger bugs and inconcistencies on same and different platforms)
…note that the “never resolve outside” means “outside any nested withs” (this is in practice the same as using multiple comma separated identifiers on the with parameter). Using nested withs is a risky choice, but it’s still a choice one may conciously make (including to do minimal changes to existing Pascal code).
One could use multiple dots like mentioned above in FlashPascal example, to make it even simpler for the compiler and the reader of the code (that is choose to only resolve at the with level the dot prefix count says). This can help also when having items in nested withs that have the same name for a field. Some might say that a dot could be lost easily when refactoring / editing other person’s code in such cases, but code comments can help like in the example above
Just roll your own with(.) statements One dot vs a dozen dots. So a safe with statement needs to go out of the form’s scope. You can pass a control or two to routine in a routines.unit that has no globals or scope in the unit to add side effects to controls passed. Or one can try in lining the code to see if it can be inlined: that quickly finds scope issues. Here’s an anonymous method stab at it.
Form
...
var
LSafeWith: TProc;
begin
// assign var LSateWith's Procedure //
LSafeWith := ( procedure begin with EB.Popup do //EB Event Boss unit
for var I: Integer := 0 to items.count - 1 do
memo1.lines.add(Tcontrol(items[I]).tag.ToString + ' ' + items.Caption + ' ' + items.Name);
end );
//run it local
//LSafeWith;
EB.WithSafe(LSafeWith); // EB runs the above Safewith inside the form's codebase :( Stiil a good exercise to improve scoping methods.
One thing that would be nice the mini map could light up like a Yule time tree, rather than modest warning markers now.
Hmmm, to me that looks more complicated than
with
which was designed to simplify individual statements, ostensibly to increase readability, but in practice turned out to decrease comprehension (or at least set the stage for confusion) for some developers. Putting things in anonymous methods just save usingwith
seems impractical and definitely not beginner friendly. 😁All “with” opinions aside, inline variables could have been very handy if they would not always break IDE refactoring. It would have been trivial in the code sample above to formally declare “B” as a local variable rather than promote the newer inline variable syntax not supported by the IDE.