About the new Delphi 13 “magic” routine NameOf (also UnitName)

NameOf is a magic function introduced in Delphi 13. What it does is return the identifier name of a symbol (routine, variable, etc) as a string, as resolved by the compiler. It can be used for:

 

Consistent naming

Its main advantage is that if we later rename the function, the compiler forces the change in all NameOf references, otherwise the code won’t compile:

procedure TForm2.Button1Click(Sender: TObject);
begin
// Shows "Error in Button1Click"
ShowMessage('Error in ' + NameOf(TForm2.Button1Click));
end;

With a plain string literal like the one below, you can easily forget to change all instances, and the code will compile!

 

procedure TForm2.Button2Click(Sender: TObject);
begin
// Shows "Error in Button1Click" (which is wrong)
ShowMessage('Error in Button1Click’);
end;

Another example:

procedure TForm2.Button1Click(Sender: TObject);
begin
var i:= 42;
ShowMessage('Variable ' + NameOf(i) + ' = '+ IntToStr(i));
// Shows “Variable I = 42
end;
Assert(Assigned(FConnection), NameOf(FConnection) + ' is not initialized');
Log(NameOf(UserName) + ' = ' + UserName);
RegisterAction(NameOf(Button1Click), Button1Click);

Try/Except

 

Another usage is in try/except. I have encountered programs that abused try/except to swallow exceptions. I wanted to batch insert logging between try/except blocks to know where the program (silently) crashed. Now I can do something like:

Procedure DoGood;
try
  DoSomething;
except
  on E: Exception do
      Log('Exception in ' + NameOf(DoGood) + ': ' + E.Message);
end;

If the routine name gets renamed, the message stays consistent.

 

Limitations

Note that there are also limitations, e.g. it only works on identifiers, not arbitrary expressions, and it doesn’t return fully qualified names unless you give them.

For example, this code will not compile:

procedure TForm2.Button1Click(Sender: TObject);
begin
  ShowMessage('Error in ' + NameOf(Form2.Button1));
End;


Unfortunately, you cannot use the new (Delphi 13) NameOf() to show the unit’s name. However, you can use UnitName and UnitScope which are class methods of TObject.

Example:

procedure TMyClass.Button1Click(Sender: TObject);
begin
ShowMessage('Error in: ' + UnitName + '. Scope: ' + UnitScope);
end;

Limitations

Because UnitName is a method of TObject, it only works if the unit has a class (deriving from TObject – but in Delphi all classes derive from TObject) inside. If the unit has no class, add a fake class like:

type
TFake = class
end;
…
ShowMessage('Current unit is: ' + TFake.UnitName);

Other cool tricks

You can show at runtime where a class resides:
ShowMessage('TButton lives in: ' + TButton.UnitName);

Leave a Comment

Scroll to Top