LRN: Quick Tips – Fields and Symbols

One recurring problem I’ve seen people run into with Roslyn is working with fields and symbols. Consider the following:

class MyClass
{
int myField = 0;
public int MyProperty {get; set;}
public void MyMethod() { }
}

view raw
gistfile1.cs
hosted with ❤ by GitHub

The above program consists of a ClassDecarationSyntax with child FieldDeclarationSyntax, PropertyDeclarationSyntax and MethodDeclarationSyntax.

In previous blog posts, we discussed how we could use SemanticModel.GetDeclaredSymbol(SyntaxNode) to retrieve the symbol for pieces of declaration syntax. So it would make sense if we could get the symbols for our field, property and method with the same approach.

Typically one would try the following:

var tree = CSharpSyntaxTree.ParseText(@"
class MyClass
{
int myField = 0;
public int MyProperty {get; set;}
public void MyMethod() { }
}");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
//Get declarations
var property = tree.GetRoot().DescendantNodes().OfType<PropertyDeclarationSyntax>().Single();
var method = tree.GetRoot().DescendantNodes().OfType<PropertyDeclarationSyntax>().Single();
var field = tree.GetRoot().DescendantNodes().OfType<FieldDeclarationSyntax>().Single();
//Get symbols
var propertySymbol = model.GetDeclaredSymbol(property);
var methodSymbol = model.GetDeclaredSymbol(method);
var fieldSymbol = model.GetDeclaredSymbol(field);

view raw
gistfile1.cs
hosted with ❤ by GitHub

However, there’s a problem here. fieldSymbol is null! Our approach worked for methods and properties, but didn’t for fields. The reason for this is actually quite simple:

Fields can contain multiple symbols.

For example:

class MyClass
{
int myField1, myField2, myField3;
}

view raw
gistfile1.cs
hosted with ❤ by GitHub

This is even clearer when we look at the syntax tree (I’ve omitted tokens and trivia).

fieldTree

What symbol could be returned for the above FieldDeclarationSyntax? In order to access these symbols we instead look at the individual variables within the field as shown below:

var tree = CSharpSyntaxTree.ParseText(@"
class MyClass
class MyClass
{
int myField1, myField2, myField3;
}");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
var field = tree.GetRoot().DescendantNodes().OfType<FieldDeclarationSyntax>().Single();
foreach (var variable in field.Declaration.Variables)
{
//Now we can access each of the symbols within the field
var fieldSymbol = model.GetDeclaredSymbol(variable);
}

view raw
gistfile1.cs
hosted with ❤ by GitHub

It turns out fields are not the only “special syntax” that cannot be converted into a symbol. If you’re interesting, you can see them all online on the Roslyn Reference Source. They are:

  • Global Statements – Global statements don’t declare anything, even though they inherit from MemberDeclarationSyntax.
  • IncompleteMembers – Incomplete members don’t declare any symbols.
  • Event Field Declaration – Can contain multiple variable declarators. GetDeclaredSymbol should be called on them (the declarators) directly.
  • Field Declaration – Can contain multiple variable declarators. GetDeclaredSymbol should be called on them (the declarators) directly.

This bit me in August and I submitted an Issue to the Roslyn team about this. I originally thought an exception should be thrown in these cases, but I’ve since changed my mind. Instead, I think there needs to be clearer documentation on the GetDeclaredSymbol() function. It also might be appropriate for someone to create an analyzer that detects when people do this and warn them.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s