Skip to main content
Thoughts from David Cornelius

Category

The Delphi Debate series continues with another topic that has been discussed back and forth for ages. This time, instead of a procedure or function in the RTL, we are talking about three reserved words: the with, goto, and label statements which pre-date Delphi--they are part of the core Pascal language itself!

WITH

The with statement in Delphi is a shorthand for accessing methods and properties of an object. An argument for using it is that it shortens the length of a line of code thus making the code more readable. For example:

procedure TMyForm.UpdateInventoryItem(const NewQty: Integer);
begin  
  dmStoreInventoryData.tblUpdateItemForStore.Edit;
  dmStoreInventoryData.tblUpdateItemForStore.FieldByName('Qty').AsInteger := NewQty;
  dmStoreInventoryData.tblUpdateItemForStore.Post;
end;

Can be rewritten like this:

procedure TMyForm.UpdateInventoryItem(const NewQty: Integer);
begin
  with dmStoreInventoryData do begin
    tblUpdateItemForStore.Edit;
    tblUpdateItemForStore.FieldByName('Qty').AsInteger := NewQty;
    tblUpdateItemForStore.Post;
  end;
end;

Furthermore, you can nest with statements:

procedure TMyForm.UpdateInventoryItem(const NewQty: Integer);
begin
  with dmStoreInventoryData do begin
    with tblUpdateItemForStore do begin
      Edit;
      FieldByName('Qty').AsInteger := NewQty;
      Post;
    end;
  end;
end;

Now, disregarding the fact we added four "scaffolding" lines, the three original lines look rather succinct; but even if you're the only programmer who deals with this code, there will come a point in time in the future where you question whether you're calling an Edit method of the data module or the table object in the data module. Worse, what if you forget about this with statement and add an Edit or Post method to the data module? Without touching this code, you've just broken it!

There are much better ways of handling coding tasks like this. In this small example, experienced programmers would quickly point out that a table object should not be accessed outside the data module but a procedure should be created in the data module that deals with the local table object, thus reducing the object layer by one level and thus the "need" to use a with statement. Of course, there are many other ways and this is just one example, but that's a topic for another day.

Generally, the with statement is strongly frowned upon by experienced programmers in the Delphi community, and I agree--with rare exceptions.

Sometimes, I need to pop up a form to request some simple option from the user, returning a single item picked from a list (for example). So I create a dialog form, remove it's global variable and add a public class function like this:

class function TfrmFilterSKUs.GetSKUFilter: string;
begin
  with TfrmFilterSKUs.Create(Application.MainForm) do
    try
      if ShowModal = mrOK then
        Result := lbCategories.Items[lbCategories.ItemIndex]
      else
        Result := EmptyStr;
    finally
      Free;
    end;
end;

This is taken straight out of one of my projects and while I recommend against using with on general principles, I would say that IF you're the only programmer maintaining the code and IF the section of code is very small and IF you never use more than one object in a with and IF you never, ever nest them, then in this very limited use case, they are handy.

GOTO & LABEL

I am surprised--and horrified--to know that goto still exists in the Delphi language. I had to look up the syntax because I honestly didn't even know how to use it--or its corresponding label statement. 

The goto statement makes an immediate jump of the execution point to another spot in the code identified by its pre-defined label. It can jump into or out of a for-loop, past an object creation statement, create unidentifiable infinite loops, and all sorts of other very bad things. One article says, "it's like a ticking time bomb."

Delphi's roots are in Pascal which was designed to "encourage good programming practices using structured programming" (quoted from Wikipedia) and I imagine part of that was to wean people off using line numbers and GOTO statements that were so prevalent in BASIC code, which was introduced six years earlier and quickly became the defacto programming language on many systems both large and small. So to include a way to perpetuate spaghetti code perplexes me. Perhaps it was unthinkable in the day to design a language without a way to jump directly to another location in code like you could in assembly, or perhaps Niklaus Worth wanted to provide a way that people could get productive right away if they had code they wanted to port to help make the language popular (I have nothing to back this up).

Whatever the reason, it can't be removed now without possibly breaking some old code (although maybe that type of code should be broken!).

I hope there's no doubt remaining where I stand on this: Don't use goto. Ever.

Exit

Now I want to throw one more into the mix because it ties in very closely with goto--but I'm not nearly as adamant against using it. The system procedure Exit in Delphi is not a reserved word but acts like a goto in a method with an implied label at the end of the method; more simply, it exits from the current procedure or function. Similar to the goto, it breaks out of standard control structures and can easily be missed in a long method. There are two redeeming qualities (in my opinion): first, it always exits the method--you don't have to look around for the label to figure out where program execution goes; and second, it can't jump into the middle of another block of code.

From what I've seen, the most frequent use of Exit is to check for some constraints and if something fails, simply skip processing the rest of the code in the procedure. For example if you're opening a database table and checking the connection but find it's not accessible, then just call Exit rather than fall through a proper if-else structure. This can simplify things by keeping control structure code out of the way of what the method is trying to do but isn't that big of an advantage if written well.

This question was posed on StackOverflow several years ago and there were some good points made in the answers. I find myself siding with those who prefer a single exit point by using control structures and avoiding arbitrary jumps, even if it's just an innocent call to the convenient Exit procedure.

Thomas Müller (not verified) Wed, 07/13/2022 - 02:48

You missed two more goto like statements:

  • Continue
  • Break

They are basically gotos with a hard coded label at the end and after a loop respectively. Unlike Exit I don't use them as they make it difficult to understand the flow of execution, in particular in nested loops.

As for not using goto at all: I have to admit that there is exactly one goto in my whole code base where I simply couldn't think of a better way to implement what I needed.

david Wed, 07/13/2022 - 14:51

In reply to by Thomas Müller (not verified)

I briefly considered adding break and continue to the conversation but felt they are part of the loop control structure and (IMHO) not as "bad" as the goto-like constructs. Also, looping code is much more concise so anything that breaks out will be more visible and you're unlikely to skip large sections of code like an Exit at the top of a procedure can. Of course, these are generalities and cases could be made either way.

Thanks for mentioning this!

IanB (not verified) Tue, 07/26/2022 - 13:25

Did you know you can do this too?

 with dmStoreInventoryData, tblUpdateItemForStore do
 begin
      Edit;
      FieldByName('Qty').AsInteger := NewQty;
      Post;
  end;


Note I say "can" but not "should" or indeed "ever". 

Add new comment

The content of this field is kept private and will not be shown publicly.