nameof — o nome como texto verificado
nameof(X) vira o nome de X como constante de texto; renomear sem atualizar quebra a compilação em vez de deixar texto velho.
O que é e por que existe#
Mensagens de erro, nomes de coluna e logs costumam repetir à mão o nome de um campo ou parâmetro. Quando o campo é renomeado, o texto fica desatualizado e ninguém percebe. nameof(X) devolve, em compilação, o nome de X como constante de texto, e o compilador confere que X existe. Renomeou sem atualizar? Erro de compilação, e não um texto mentiroso.
if AValue < 0 then
WriteLn('erro: ', nameof(AValue), ' não pode ser negativo');X não é avaliado e não gera código.
O que pode ser X#
Tipo (inclusive embutido e apelido), variável, parâmetro, campo, property, método, rotina, constante, valor de enum, módulo (System.Remainder dá Remainder), tipo genérico (TList<Int32> dá TList), método genérico, e caminhos qualificados como TFoo.Bar, Obj.Campo, Self.FCampo, Unit.Simbolo. Dos qualificados vale o último componente.
program NameOf;
// Demo 34: nameof - o nome de um símbolo como texto, em tempo de compilação.
//
// Escrever 'Idade' à mão num texto é frágil: se renomear o campo, o texto
// fica desatualizado. nameof(Idade) é verificado pelo compilador (nome
// inexistente é erro) e acompanha as renomeações. O argumento NÃO é executado.
type
TColor = (Red, Green, Blue);
TCustomer = class
public
Age: Int32;
function Validate(AValue: Int32): Bool8;
end class;
function TCustomer.Validate(AValue: Int32): Bool8;
begin
if AValue < 0 then
begin
// mensagem de erro que cita o parâmetro sem escrevê-lo "no braço"
WriteLn(' erro: ', nameof(AValue), ' não pode ser negativo');
Exit(False);
end if;
Result := True;
end function;
const
cTypeName = nameof(TCustomer); // vale até em constantes
var
LCustomer: TCustomer;
begin
WriteLn(nameof(TCustomer));
WriteLn(nameof(TCustomer.Age)); // caminho qualificado
WriteLn(nameof(TCustomer.Validate)); // método
WriteLn(nameof(Int32)); // tipo embutido
WriteLn(nameof(TColor.Green)); // valor de enumeração
WriteLn(cTypeName);
LCustomer := TCustomer.Create;
WriteLn('validou -5? ', LCustomer.Validate(-5));
// nameof(LCustomer.Age) não lê o campo: só devolve o nome
WriteLn('variável: ', nameof(LCustomer), ' campo: ', nameof(LCustomer.Age));
end.TCustomer
Age
Validate
Int32
Green
TCustomer
erro: AValue não pode ser negativo
validou -5? FALSE
variável: LCustomer campo: AgeDemo 34. Tipo, campo, método, tipo embutido, valor de enum, constante e variável; nada é executado.
O resultado é um literal de texto comum: converte para UString/LString conforme o contexto, concatena, entra em const, em argumento de atributo ([Alias(nameof(TFoo))]), em static_assert e em interpolação. No {$if}, vem já comparado com um texto: {$if nameof(TFoo.Campo) = 'Campo'}.
Erros típicos#
- Nome inexistente:
FE2070("nameof: "Totall" não está declarado"). Outros da família:FE2071aFE2078. - Não valem chamada, índice, literal nem operador (
FE2073/FE2075). nameofé contextual e só em minúsculas: só é o operador quando vem seguido de(e nenhum símbolo visível tem esse nome.NameOfeNAMEOFsão identificadores comuns.- Dentro de um genérico,
nameof(T)dá o nome do tipo da instância (Int32), porque o corpo é reescrito com o argumento antes de ser lido.
Ferramentas#
O argumento conta como uso do símbolo: o fc --symbols (achar referências, renomear) e o aviso de "declarada e nunca usada" o enxergam. Por isso o renomear do editor atualiza o nameof junto.
Veja também#
- Metaprogramação, static_assert.
- Demo
34-nameof.