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);