About a year ago, I wrote about Delphi: you should avoid the with statement as it makes your code less future proof. Then I already tweeted I would follow up. Time to do it now (:

Besides my first post, these links inspired me most:

Posts about the with statement usually cause a stir: people either like or dislike it with passion.

Starting with some history and examples, this posts lists a few DOs and DON’Ts when using the with statement, shows advantages and drawbacks, and shows you tools to eliminate with statements.

Then

No wonder people are passionate about the with statement: it has been a feature ever present in Pascal. It was introduced with reason, but it also has drawbacks. Though briefly mentioned in the 1983 “Standard Pascal User Reference Manual” Paperback by Doug Cooper (Look Inside the Standard Pascal User Reference Manual: Doug Cooper: 9780393301212: Amazon.com: Books, then search for “with”) it was a very important addition at that time: 1970s and 1980s.

Remember Turbo Pascal 1.0 in 1983 being one of the first products having an IDE, but not even a debugger?

Basically back then, your best editor could do almost nothing so anything that saved typing lots of text was a big plus.

And with would potentially save a lot of text.

“Dull” text that replaced the full access path of relatively simple situations: record, or maybe nested records.

Back then you had very simple scope: records only held storage. Methods were relatively straight forward global functions or procedures.

Now

Fast forward to now:

classes, namespaces, units that make scoping a truckload more complex

excellent IDEs available for almost any programming language. Syntax highlighting, code completion, context sensitive editors, and more advanced features save you most of the typing.

To get a feel of the DOs and DON’Ts about the Delphi with statement.

Let’s start with this simple VCL example:

procedure TForm1.Edit1Click(Sender: TObject); begin with Edit1 do begin ShowMessage(Format('%s: %s', [Name, Caption])); end; end;

You might think that this code shows something like “Edit1: Edit1”. But it shows “Edit1: Form1”, as a TEdit does not have a Caption property (it has a Text property), but TForm has a Caption property.

procedure TForm1.Button1Click(Sender: TObject); begin with Button1 do begin ShowMessage(Format('%s: %s', [Name, Caption])); // put a breakpoint here end; end;

More drawbacks

Put a breakpoint on the indicated line, then observe the debugger takes the Name and Caption properties from Form1, not of Button1. You can find another example of this debugger behaviour here.

with also can prevent some refactorings (both the stock Delphi ones, and ones available through for instance ModelMaker Code Explorer and Castallia).

Nesting

Red lights should start flashing when you add multiple clauses into one with statement, as that usually widens up the scope so much that in stead of benefiting from it, you will feel pain as If With is Considered Harmful, What About Double With? shows: where does Enabled belong to?

with LMargins, GlassFrame do begin if Enabled then begin if not SheetOfGlass then begin cxLeftWidth := Left; cxRightWidth := Right;

Sometimes, the use of with is sign of a need for refactoring, for instance shown in Is Delphi “with” keyword a bad practice?:

procedure TMyForm.AddButtonClick(Sender: TObject); begin with LongNameDataModule do begin LongNameTable1.Insert; LongNameTable1_Field1.Value := 'some value'; LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; LongNameTable1.Post; end end;

The above code has a big dependency on the LongTableDataModule and is business logic. So this business logic should be refactored out of the form into the data module. Not doing so violates Law of Demeter, which is basically about Loose Coupling.

procedure TLongNameDataModule.AddToLongNameTable1(const NewField1Value: string); begin LongNameTable1.Insert; LongNameTable1_Field1.Value := NewField1Value; LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; LongNameTable1.Post; end;

Then call it from the form like this:

procedure TMyForm.AddButtonClick(Sender: TObject); begin LongNameDataModule.AddToLongNameTable1('some value'); end;

This effectively gets rid of the with statement, and makes the code more maintainable: kill two birds with one stone.

One of the few places I still use with is like here (thanks Jamey McElveen):

with TMyForm.Create(nil) do try ShowModal(); finally Free(); end;

and here (thanks Brian Frost):

procedure ActionOnUpdate(Sender: TObject) begin with Sender as TAction do Enabled := Something; end;

Alternatives

Both VB6 (Visual Basic) and Oxygene have alternatives for the with statement. It is still there in VB.NET, and still raises controversies.

I didn’t know it came from SmalTalk, but I did know C# 3 introduced a very confined similar construct for initialization.

Back to Visual Basic: in VB6, you have to prepend all the usage with a dot (.) which used in Delphi code would make your code look like this:

begin with MagicalFrame.Label1 do begin if .Font.Color = clBlue then begin .Font.Color := clRed; .Caption := 'Red'; end else begin .Font.Color := clBlue; .Caption := 'Blue'; end; end; end;

In Oxygene, you can introduce a temporary variable in a with statement.

Eliminating with

I know of two tools to eliminate the with statement.

I have a lot of experience with the latter, but Castallia should work just as good.

–jeroen