The New nameof Expressions in C# 6: Painless Conversion of Symbols to Strings
One of the more specific of the new features in C# 6, the nameof expression replaces a C# symbol with the string representation of that symbol. Many of the idioms we use in modern development—reflection, late binding, wire-transfer protocols like JSON, logging scenarios—require us to translate symbol names into text. Copying the symbol name and putting it inside quotes to create a string; it's so easy that we do it without thinking—and that's the problem. When we write code without thinking, we make mistakes. Sometimes we catch those mistakes immediately. Sometimes we catch them with tests. And sometimes we don't catch them until after the product ships.
In previous versions of C#, options were limited. We copied symbol names, or we wrote dynamic code that used expressions and reflection to find the name of a symbol. That dynamic code was error-prone and came with significant runtime performance costs. The Windows 8 Store templates used the CallerMemberName attribute to build a reusable feature that would raise the correct PropertyChanged events in a view. Even this had runtime costs.
Overall, we expended a lot of brainpower to convert C# symbols to strings. All those different techniques used a runtime conversion, and therefore came with significant runtime costs. Converting symbols to strings started to look like a great language feature.
Introducing nameof
The new nameof expression in C# 6 addresses these concerns. It takes an expression and evaluates to the string representation of the local name of that expression. The canonical example is raising the PropertyChanged event in a class that implements INotifyPropertyChanged:
public class Person : INotifyPropertyChanged
{
public string FirstName
{
get
{
return firstName;
}
set
{
if (value != firstName)
{
firstName = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(FirstName)));
}
}
}
private string firstName;
public event PropertyChangedEventHandler PropertyChanged;
// more elided
}
In the example above, the nameof expression generates the string used in the constructor to the PropertyChangedEventArgs object. (Notice that I'm also using the new ?. operator I discussed in "Using the New Null Conditional Operator in C# 6.") Using the nameof operator eliminates any chance of mistyping the property name. If I mistype the symbol name, the compiler complains, and I must fix it. The symbol has semantic meaning.
Making the compiler prevent you from mistyping a property name is only the beginning. Equally important, any static analysis tools you use can also benefit from having the symbol information, rather than just a text string. Figure 1 shows the code from the preceding example, after I've used the Rename refactoring (F2) to change the property name from "FirstName" to "GivenName". Notice that the refactoring tool finds and highlights the symbol in the nameof expression. Because it's a valid C# symbol, not a string literal, the symbol participates in all of the analysis.
The refactoring tools and the compiler validation show why this feature is important for modern developers. The size of our modern codebases mandates using software tools to help us manage our software development activities. Those tools often rely on static analysis of our software to understand our code and to suggest where we may have made mistakes.
Also, unlike earlier attempts to convert symbols to human-readable text, the nameof expression involves no runtime cost. The nameof expression is evaluated at compile time, and the generated code contains the replacement text.
Valid nameof Expressions
In the examples to this point, I've used the property name as the expression. You can qualify the expression with the type name, if that's more readable to you:
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Person.FirstName)));
The compiler generates text for the rightmost name. In this case, it generates "FirstName", not "Person.FirstName". It follows that you can use the fully qualified name, including any namespaces:
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(ExampleNamespace.ExampleCode.Person.FirstName)));
The result is still the string "FirstName".
The argument to nameof must resolve to a named expression. I've used property names in the examples so far, but several other named expressions are also valid. You can use local variables. Non-generic class names, struct names, and delegate types are also legal.
Anonymous types, numeric literals, and string literals, on the other hand, are not legal. They produce CS 8081, "Expression does not have a name". The same is true for lambda expressions.
Generics are an interesting case. You can use closed generic types anywhere, if every type parameter has been specified. For example:
nameof(List<int>)
This expression evaluates to "List". The type parameters are omitted from the generated string.
You can use a generic type definition (where the type parameters are placeholders, such as in List<T>) only inside a generic type definition that has the same named-type parameter. I couldn't use List<T> inside the Person class shown in my examples, but I could use IEnumerable<T> inside a class like List<T>, where the 'T' type parameter has been declared.
Those rules might seem a bit hard to comprehend when written in English, but they're reasonable as you write code. Just remember that the argument to the nameof expression must have a name, and the symbol must resolve at compile time. Just as you can't declare a variable of the type List<T> outside a generic definition (where T is a type parameter), you can't use List<T> in a nameof expression when T has not been defined.
Some Initial Guidance on nameof, and a Look into the Future
As with the other new features I've discussed in previous articles, I've made it a habit to use the new feature in new code that I write. It helps me to create correct code and, in this case, to avoid simple mistakes that come from translating program symbols to text by hand.
However, with this feature, I'm not updating my existing code very aggressively. I don't update existing code with the nameof expression unless I already have tests surrounding that code, to verify that its behavior is correct. It may seem a reasonable assumption to correct strings that you see in code. But in many cases a text string may be different from a program symbol name for good reason. It may be easy to make all those changes. However, be careful to make sure that the text is intended to match the symbol. It may be a coincidence, rather than part of a design. That means being cautious about any changes.
I'm more excited about what might happen in future releases. In Visual Studio 2015, any of the tools and features work with C#—and only C#. In future releases, I'd like to see the tools extend to places where C# interacts with other languages. This could be a huge help in XAML bindings, or bindings in Razor syntax. One day, maybe it could even extend to matching names in C# with bindings in view models in an Angular-based application. None of these features are promised, but they would be welcome.