LRN Quick Tips – Working with nameof

The nameof operator has gone through five iterations as the Roslyn team worked to nail down its syntax and semantics. Now that the design of the nameof operator has been finalized, we can look at some simple examples.

Within C#, nameof is a contextual keyword. This means there is no way to distinguish the nameof keyword from a call to a method that happens to be named nameof.

Lucian Wischik elaborates:

In C#, nameof is stored in a normal InvocationExpressionSyntax node with a single argument. That is because in C# ‘nameof’ is a contextual keyword, which will only become the “nameof” operator if it doesn’t already bind to a programmatic symbol named “nameof”

Identifying nameof Expressions

This means we can only identify nameof expressions at the semantic level. We do so by finding all invocations to “nameof” that do not bind to any symbol. These invocations must also be standalone (ie. not part of a member access like MyClass.nameof())


var tree = CSharpSyntaxTree.ParseText(@"
class C
{
void M()
{
int variable = 0;
//Does not bind to a symbol (as there is no class called MissingClass)
//but it is not a true nameof expression
MissingClass.nameof(x);
}
}");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
var nameofInvocations = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>();
var validNameOfs = nameofInvocations.Where(n => n.Ancestors().OfType<MemberAccessException>().Count() == 0);
foreach (var validNameOfSyntax in validNameOfs)
{
if (model.GetSymbolInfo(validNameOfSyntax).Symbol == null)
{
//validNameOfSyntax is the nameof operator
Console.WriteLine("We've found a nameof!");
}
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

As with all contextual keywords, it’s a bit of a pain to work with. But that’s the price we pay for backwards compatability.

Creating a nameof expression

Previous versions of the Roslyn API allowed for direct creation of a NameOfExpressionSyntax. Now we must create an InvocationExpressionSyntax with the identifer “nameof”.

For example, we can generate the following nameof expression:

string result = nameof(result);


var nameOfExpression = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(
SyntaxKind.StringKeyword)))
.WithVariables(
SyntaxFactory.SingletonSeparatedList<VariableDeclaratorSyntax>(
SyntaxFactory.VariableDeclarator(
SyntaxFactory.Identifier(
@"result"))
.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName(
@"nameof"))
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList<ArgumentSyntax>(
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName(
@"result"))))))))));

view raw

gistfile1.cs

hosted with ❤ by GitHub

Side Note: As a mere mortal, I have no idea how to work with the SyntaxFactory API. I generated the above code with Kirill Osenkov’s fantastic Roslyn Quoter tool.

The important takeaway is that (at the syntax level) nameof expressions are no different than regular invocations.

4 thoughts on “LRN Quick Tips – Working with nameof

    1. Well. it could.

      The problem here is that x is being recursively defined. Same as Func f = n => n > 1 ? n * f(n – 1) : 1;

      On the other hand, the value of namoeof(x) is taken from the source code and not the metadata. No matter what the type of x is, nameof(x) will always be “x”. x might not even exist. As long as the syntax was valid, it would work. But it would not be a valid expression, which nameof requires.

  1. First of all, thanks for entire LRN series. It helped me a lot.

    In the last code snippet in this post, you are showing how to generate `nameof` expression via SyntaxFactory. It seems to be correct up util the point you try to read diagnostics from compilation or `SemanticModel`. You’ll immediately get:
    “Error CS0103: The name ‘nameof’ does not exist in the current context”

    That is, because you need to explicitly mark the “nameof” identifier token as a contextual `nameof` keyword. Otherwise Roslyn will never try to bind the name as the keyword.

    SyntaxFactory.Identifier
    (
    SyntaxFactory.TriviaList(),
    SyntaxKind.NameOfKeyword,
    “nameof”,
    “nameof”,
    SyntaxFactory.TriviaList()
    )

    More on why and how to do that is here:
    https://stackoverflow.com/q/46259039/1560190

Leave a reply to Paulo Morgado Cancel reply