Back to Blog
C#

C# nameof Keyword: Compile-Time String Names

c# nameof keyword: Learn how the C# nameof keyword produces compile-time string names, prevents magic strings, and keeps validation and logging code refactoring-safe.

nameof operatorC# 6compile-timerefactoringargument validationmagic strings
Illustration of the C# nameof operator converting a code symbol into a compile-time string literal, representing refactoring-safe string names.

c# nameof keyword requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The nameof keyword in C# — technically an operator introduced in C# 6 — evaluates to the string name of a symbol at compile time. When you write nameof(customer), the compiler substitutes the literal string "customer" into the compiled IL. This means the expression has no runtime cost, and it stays correct when you rename the symbol.

What the nameof Operator Returns

The operator accepts any symbol that can be referenced in code: local variables, parameters, fields, properties, methods, types, and namespaces. The result is the source-level name of that symbol as a string literal.

string firstName = "Ada"; string variableName = nameof(firstName); // "firstName" string typeName = nameof(Customer); // "Customer" string propertyName = nameof(Customer.Name); // "Name"

For a type, nameof returns the simple name, not the fully qualified name. nameof(System.Text.StringBuilder) produces "StringBuilder". If you need the fully qualified name, you must combine nameof with typeof and the type's FullName property, which is a runtime operation.

Using nameof for Argument Validation

The most common production use of nameof is in guard clauses. Before nameof existed, developers wrote string literals for parameter names in exception messages:

public void Save(Customer customer) { if (customer == null) { throw new ArgumentNullException("customer"); } }

If the parameter is renamed, the string literal silently becomes wrong. The exception message then references a parameter that no longer exists, which confuses anyone debugging the call site. With nameof, the message follows the rename automatically:

public void Save(Customer customer) { if (customer == null) { throw new ArgumentNullException(nameof(customer)); } }

This pattern also works for ArgumentException, ArgumentOutOfRangeException, and similar exceptions that take a parameter name. The compiler verifies that the symbol exists, so a typo becomes a compile error rather than a misleading runtime message.

nameof in Property Change Notification

In MVVM applications, INotifyPropertyChanged implementations historically relied on string literals in PropertyChanged invocations:

public string FullName { get => _fullName; set { _fullName = value; OnPropertyChanged("FullName"); } }

If the property is renamed, the string literal is not updated by refactoring tools. The event still fires, but subscribers listening for the old name never receive the notification. Using nameof ties the notification to the actual property symbol:

public string FullName { get => _fullName; set { _fullName = value; OnPropertyChanged(nameof(FullName)); } }

When the property is renamed, the nameof expression updates automatically, and the compiler catches any reference that was not updated. This is especially valuable in large view models where a missed rename would otherwise surface as a subtle binding bug that only appears at runtime.

nameof in Logging and Diagnostics

Logging code frequently needs to record which method, property, or parameter is involved. Hardcoded strings in log messages drift from the actual code as it evolves. nameof keeps the two in sync:

public async Task<Order> GetOrderAsync(int orderId) { _logger.LogInformation("Entering {Method} with orderId {OrderId}", nameof(GetOrderAsync), orderId); // ... }

The same principle applies to switch statements that branch on a type or member name, dictionary keys built from property names, and serialization code that needs to match a known member name. In every case, nameof gives you a compile-time-checked string that cannot silently diverge from the symbol it describes.

Edge Cases and Limitations

nameof does not work with runtime values. You cannot write nameof(someVariable.Value) and expect the property name of the value; the expression must reference a symbol that the compiler can resolve statically.

For generic types, nameof(List<int>) returns "List", not "List<int>" and not "List1". The operator strips generic type arguments. If you need the full generic display name, you must use typeof(List<int>).Name, which at runtime produces "List1" — a different string that requires additional formatting.

Nullable value types behave similarly: nameof(int?) produces "int", not "int?". The operator returns the underlying type's simple name.

Verbatim identifiers are normalized. If a variable is declared as @class, nameof(@class) returns "class" without the @ prefix. The result is the identifier as it appears in source, minus the verbatim marker.

One important limitation: nameof gives you the simple member name, not the full path. nameof(Customer.Address.Street) returns "Street". If you need "Customer.Address.Street", you must compose it manually from multiple nameof calls.

Runtime Cost and Compile-Time Behavior

Because nameof is resolved by the compiler, the generated IL contains a string literal. There is no reflection call, no method invocation, and no runtime lookup. The cost is identical to writing the string literal by hand.

This matters in hot paths. A property setter that calls OnPropertyChanged(nameof(SomeProperty)) performs no extra work compared to passing a literal string. The same applies to logging calls, where the nameof expression is evaluated before the logging framework decides whether the message should be emitted.

The compile-time nature also means nameof cannot be used in contexts that require a runtime expression, such as dynamic dispatch with dynamic objects. If the symbol cannot be resolved at compile time, the expression will not compile.

When nameof Does Not Help

nameof only protects against symbol renames that occur in the same compilation. If you persist a member name as a string in a database, a configuration file, or a serialized payload, and then rename the member in a later version of the application, nameof does not migrate the stored value. The string in storage remains the old name, and the application must handle the mismatch explicitly.

Similarly, nameof does not help with reflection scenarios where the type is discovered dynamically at runtime. If you load a type by name from an assembly and need to invoke a member, the member name must come from configuration or a mapping table, not from a compile-time expression.

For these cases, the practical approach is to define the member name in one place — a constant, a mapping dictionary, or a data contract — and reference that single source from both the code and the persistence layer. nameof can still be used to derive that constant safely, but it cannot replace the runtime lookup itself.

c# nameof keyword: Practical Usage and Code Examples | RYUSLOG DEV