The with statement has been part of Object Pascal since the early days of Pascal. It was designed to save typing by letting you omit the object name when accessing its properties and methods. In practice, with introduces subtle bugs and makes code harder to read and maintain. At GDK Software, we strongly advise against using it.
The with statement temporarily brings the members of an object or record into scope so you can reference them without a qualifier:
with MyButton do
begin
Caption := 'Click me';
Enabled := True;
Width := 120;
end;
At first glance this looks clean. The problems start when your codebase grows.
The biggest danger of with is that your code can silently change meaning when someone adds a new property to a class, including classes in the RTL or a third-party library. Consider:
with MyRect do
begin
Left := 10;
Top := 10;
Width := GetWidth; // Which Width? MyRect.Width or the enclosing form's Width?
end;
If a future version of Delphi adds a Width property to TRect (which it did), the compiler silently resolves Width to TRect.Width instead of the outer scope. No warning, no error, just broken behaviour at runtime.
The Delphi debugger cannot evaluate variables inside a with block reliably. When you hover over an identifier to inspect its value, the debugger does not know which scope to look in. This makes debugging significantly harder and costs you valuable time.
Code is read far more often than it is written. When a colleague (or future you) reads a with block, they have to mentally track which object each identifier belongs to. With nested with statements, this becomes nearly impossible:
with Order, Customer, Address do
begin
City := 'Amsterdam'; // Which object owns City?
end;
As your application evolves, new properties may be added to any of the types involved. Each such change is a potential silent bug inside a with block. The compiler will not warn you, and the problem may not surface until a customer reports unexpected behaviour.
Use a local variable to keep references concise without hiding the scope:
var Btn := MyButton;
Btn.Caption := 'Click me';
Btn.Enabled := True;
Btn.Width := 120;
The code is just as concise, every identifier is unambiguous, and the debugger works perfectly.
For records, use a local pointer or inline variable (available since Delphi 10.3):
var Rectangle: TRect := GetBounds;
Rectangle.Left := 10;
Rectangle.Top := 10;
The with statement saves a few keystrokes but introduces hidden risks: silent scope changes, broken debugger support, and hard-to-read code. At GDK Software, we never use with in production code. Replace it with a clearly named local variable and your future self and your colleagues will thank you.
For more Delphi best practices, read our article on Delphi Tips & Tricks.
Contact
GDK Software NL
(+31) 78 750 1661GDK Software UK
(+44) 20 3355 4470