One of the best new features of C# 6 was string interpolation. It is much nicer to read, and you no longer had to rely on 3rd party libraries remembering to introduce ‘DoSomethingFormat(string, params object)’ along with just ‘DoSomething(string)’.
But ‘DoSomethingFormat’ has one big advantage over string interpolation when it came to unit tests – you can assert on particular values direcly, without having to parse them out of an applied string template. If your date or number format suddenly changes, you don’t need to change your Unit Tests (provided, of course, the format itself is not important enough, just the data).
Well, good news – interpolated strings have a rarely mentioned way for you to have the best of both worlds - FormattableString type!
In the previous article we looked at the result of parsing stage of compilation - syntax graph, how it represent information available to C# compiler from analyzing a single file of code and, consequentially, reflects that text quite faithfully and how to transform it to refactor existing code. In this article we will look at symbol graph - result of the next stage of compilation - binding.
In the syntax graph you can see that core C# concepts are represented by corresponding node type, like ClassDeclaration or MethodDeclaration. Inside they often contain IdentifierTokens representing the textual name of some type, but that is as much information as you initially get. In order to get information about a particular type, like its namespace or members, you’d need to get its symbol by performing a costly binding process – check which namespaces are currently used in the file, look for type declarations through the entire solution and all imported DLLs that match, compile them if need be, etc. - compile everything.
The symbol graph allows you to answer a lot more meaningful questions than syntax graph. Want to track down all usages of a specific type? Rename something solution-wide? You can do that.
You can trigger a limited binding for a given piece of syntax graph, or you can hook into the compilation process of the IDE itself (it is continuously recompiling your solution on every change to warn you of any errors as you type away). This common way to do this is by creating a class inheriting from DiagnosticAnalyzer.
From the point of view of developer using it (we will call them 'user-dev'), Refactorings in Roslyn are additional commands that pop-up in Visual Studio when they click certain pieces of code. From our point of view, Refactorings are classes inheriting from CodeRefactoringProvider, which get a chance to examine current syntax graph every time user-dev clicks something in it and determine, if they should offer any transformations of that graph based on its state and what was clicked.
We will be building a Refactoring which allows our user-dev to regenerate a given classes public constructor by adding to it any missing assignment of members that match a certain pattern and are not yet assigned during construction. Specifically, this is the refactoring we use at work to regenerate dependency injected constructors.