CHAPTER 5
Refactoring code means reorganizing portions of code a better way, without changing the original behavior. Visual Studio 2015 dramatically enhances the refactoring experience, improving the tooling in C# and introducing support for refactoring to Visual Basic (VB) for the first time. The code refactoring tooling is built upon the .NET Compiler Platform, and as is the case for code analyzers, you can write your own domain-specific refactorings that integrate into light bulbs. In this chapter, you’ll learn how to create a custom refactoring and revisit many techniques you learned in Chapter 4. Remember: the Syntax Visualizer window is always your best friend.
Code refactorings are available when a developer manually enables the light bulb over a piece of code—for example, when right-clicking the code editor and selecting Quick Actions. Available refactorings depend on the kind of the selected syntax node. This is different from code analyzers, which report a warning or an error message by placing squiggles in the code editor and providing information in the Error List window.
You create a code refactoring using the Code Refactoring (VSIX) project template, which is available in the Extensibility node under the programming language of your choice in the New Project dialog (see Figure 34). This is actually the same location of the project template for code analyzers.

Figure 34: Creating a code refactoring project
There are many scenarios where code refactorings fit well. For instance, you can rewrite a piece of code to improve maintainability and readability, or you could automate the implementation of an interface within a class definition.
In this chapter, you’ll learn how to create a custom refactoring that targets public fields and checks whether the first letter in an identifier’s declaration is uppercase. If it isn’t, the refactoring provides a quick way to rewrite the field declaration where the first character of its identifiers are uppercase. This is useful for addressing a requirement in the .NET Framework Design Guidelines, which state that identifiers of public members should be named using the Pascal casing notation, where the first character is uppercase.
To start, create a new project using the Code Refactoring (VSIX) template and name it PublicFieldRefactoring (see Figure 34).
Note: Unlike code analyzer projects, the Code Refactoring project template does not generate a NuGet package. Therefore, the only default way to publish a code refactoring is by using a VSIX package. In Chapter 6, “Deploying Analyzers to NuGet,” you’ll learn some tricks for including a code refactoring in a NuGet package.
When you create a code refactoring project, Visual Studio generates two projects:
The project contains a sample refactoring that works against type names and offers to reverse characters in the type name. Even though it’s not very useful in real world scenarios, this can certainly be useful for instructional purposes, so it’s worth taking a look at the code. By default, code refactorings are implemented in the CodeRefactoringProvider.cs (or .vb) code file. This is a conventional file name, and can be renamed with a more meaningful name; you will typically provide meaningful file names when you add multiple code refactorings to a single project.
Tip: A code refactoring project can expose an arbitrary number of custom code refactorings. If you want to add other code refactorings to the current project, right-click its name in Solution Explorer, select Add > New Item, and in the Add New Item dialog, select the Refactoring item template available in the Extensibility node.
A code refactoring is a class that inherits from Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider, which provides the common refactoring infrastructure. Such a class must be decorated with the ExportCodeRefactoringProvider attribute, which takes two arguments: the language the code refactoring is designed for, and its name. The main entry point for a code refactoring is an asynchronous method called ComputeRefactoringsAsync, which is the place where you implement the refactoring logic. You can examine a code refactoring’s structure by opening the auto-generated CodeRefactoringProvider.cs (or .vb) file and looking at the implementation of the sample refactoring, including comments. In this chapter, you will rewrite the code refactoring from scratch, ignoring the sample offered by Visual Studio.
Before you write a code refactoring that allows replacing the first letter of identifiers in a public field with an uppercase letter, you need to understand which syntax element you need to work with, and this is something you do again with the Syntax Visualizer tool. Visual Basic and C# have different syntax to represent a field declaration, and this is important because the sample code will be slightly different for the two languages.
Figure 35 shows the Syntax Visualizer over a C# field declaration, whereas Figure 36 shows it over a Visual Basic field declaration.

Figure 35: The syntax representation of a field declaration in C#

Figure 36: The syntax representation of a field declaration in Visual Basic
These are the most important considerations at this point:
In Visual Basic, you need to iterate the FieldDeclarationSyntax.Declarators property and search for any identifier that starts with a lowercase letter; in C#, you need to iterate the FieldDeclarationSyntax.Declaration.Variables property with the same goal. Common to both languages, you need a method that converts the identifier name the appropriate way, and you need to create and register an action, similarly to what you did with code fixes in the previous chapter.
To create an action, you invoke the Microsoft.CodeAnalysis.CodeActions.CodeAction.Create static method (the same used with code fixes) and register the action, invoking the RegisterRefactoring method exposed by the refactoring context instance.
Code Listing 16 shows how to implement the ComputeRefactoringAsync method. Notice that from now on, since the code re-uses many concepts described in Chapter 4, I will not explain these concepts in the text, but detailed comments have been added to the code to aid your understanding.
Code Listing 16 (C#)
public sealed override async Task ComputeRefactoringsAsync( CodeRefactoringContext context) { // Get the root syntax node var root = await context.Document. GetSyntaxRootAsync(context.CancellationToken). ConfigureAwait(false); // Find the node at the selection var node = root.FindNode(context.Span); // Convert the node into a field declaration var fieldDecl = node as FieldDeclarationSyntax; // If it's not public, return if (fieldDecl == null || fieldDecl.Modifiers.ToFullString(). Contains("public") == false) { return; } // Used to determine if an action must // be registered bool mustRegisterAction = false; // If at least one starting character is // lowercase, must register an action if (fieldDecl.Declaration. Variables.Any(i => char.IsLower(i.Identifier. ValueText.ToCharArray().First()))) { mustRegisterAction = true; } else { mustRegisterAction = false; }
if (mustRegisterAction==false) { return; } // Create an action invoking a delegate passing // the document instance, the syntax node, and // a cancellation token var action = CodeAction.Create("Make first char upper case", c => RenameFieldAsync(context. Document, fieldDecl, c)); // Register this code action context.RegisterRefactoring(action); } |
Code Listing 16 (VB)
Public NotOverridable Overrides Async _ Function ComputeRefactoringsAsync( context As CodeRefactoringContext) As Task 'Get the root syntax node Dim root = Await context.Document. GetSyntaxRootAsync(context. CancellationToken).ConfigureAwait(False) 'Find the node at the selection Dim node = root.FindNode(context.Span) 'Convert the node to a field declaration Dim fieldDecl = TryCast(node, FieldDeclarationSyntax) 'If it's not public, return If fieldDecl Is Nothing Or fieldDecl.Modifiers. ToFullString.Contains("Public") = False Then Return End If 'Used to determine if an action 'must be registered Dim mustRegisterAction As Boolean 'Iterate the variable declarators For Each declarator In fieldDecl.Declarators 'If at least one starting character 'is lowercase, must register an action If declarator.Names.Any(Function(d) _ Char.IsLower(d.Identifier. Value.ToString(0))) Then mustRegisterAction = True Else mustRegisterAction = False End If Next If mustRegisterAction = False Then Return Else 'Create an action invoking a delegate passing 'the document instance, the syntax node, and 'a cancellation token Dim action = CodeAction.Create("Make first char upper case", Function(c) RenameFieldAsync(context. Document, fieldDecl, c)) ' Register this code action context.RegisterRefactoring(action) End If End Function |
The next step is to generate a new syntax node for the selected field declaration, supplying updated variable identifiers with the first letter, now uppercase. This is demonstrated in Code Listing 17.
Code Listing 17 (C#)
private async Task<Document> RenameFieldAsync(Document document, FieldDeclarationSyntax fieldDeclaration, CancellationToken cancellationToken) { // Get the semantic model for the code file var semanticModel = await document. GetSemanticModelAsync(cancellationToken). ConfigureAwait(false); // Get the root syntax node var root = await document.GetSyntaxRootAsync(); // Get a list of old variable declarations var oldDeclarators = fieldDeclaration.Declaration. Variables; // Store the collection of variables var listOfNewModifiedDeclarators = new SeparatedSyntaxList< VariableDeclaratorSyntax>(); // Iterate the declarators collection foreach (var declarator in oldDeclarators) { // Get a new name var tempString = ConvertName(declarator.Identifier. ToFullString()); // Generate a new modified declarator // based on the previous one but with // a new identifier listOfNewModifiedDeclarators = listOfNewModifiedDeclarators. Add(declarator.WithIdentifier( SyntaxFactory.ParseToken(tempString))); } // Generate a new field declaration // with updated variable names var newDeclaration = fieldDeclaration.Declaration. WithVariables(listOfNewModifiedDeclarators); // Generate a new FieldDeclarationSyntax var newField = fieldDeclaration. WithDeclaration(newDeclaration); // Replace the old syntax node with the new one SyntaxNode newRoot = root. ReplaceNode(fieldDeclaration, newField); // Generate a new document var newDocument = document.WithSyntaxRoot(newRoot); // Return the document return newDocument; } // Return a new identifier with an // uppercase letter private string ConvertName(string oldName) { return char. ToUpperInvariant(oldName[0]) + oldName.Substring(1); } |
Code Listing 17 (VB)
Private Async Function _ RenameFieldAsync(document As Document, fieldDeclaration As FieldDeclarationSyntax, cancellationToken As CancellationToken) _ As Task(Of Document) 'Get the semantic model for the code file Dim semanticModel = Await document. GetSemanticModelAsync(cancellationToken). ConfigureAwait(False) 'Get the root syntax node Dim root = Await document.GetSyntaxRootAsync 'Get a list of old declarators Dim oldDeclarators = fieldDeclaration.Declarators 'Store the collection of identifiers Dim listOfNewModifiedIdentifiers As _ New SeparatedSyntaxList( Of ModifiedIdentifierSyntax) 'Store the collection of declarators Dim listOfNewModifiedDeclarators As _ New SeparatedSyntaxList( Of VariableDeclaratorSyntax) 'Iterate the declarators collection For Each declarator In oldDeclarators 'For each variable name in the declarator... For Each modifiedIdentifier In declarator.Names 'Get a new name Dim tempString = ConvertName(modifiedIdentifier. ToFullString()) 'Generate a new ModifiedIdentifierSyntax based on 'the previous one's properties but with a new Identifier Dim newModifiedIdentifier As _ ModifiedIdentifierSyntax = modifiedIdentifier. WithIdentifier(SyntaxFactory. ParseToken(tempString)). WithTrailingTrivia(modifiedIdentifier. GetTrailingTrivia) 'Add the new element to the collection listOfNewModifiedIdentifiers = listOfNewModifiedIdentifiers. Add(newModifiedIdentifier) Next 'Store a new variable declarator with new 'variable names listOfNewModifiedDeclarators = listOfNewModifiedDeclarators.Add(declarator. WithNames(listOfNewModifiedIdentifiers)) 'Clear the list before next iteration listOfNewModifiedIdentifiers = Nothing listOfNewModifiedIdentifiers = New _ SeparatedSyntaxList(Of ModifiedIdentifierSyntax) Next 'Generate a new FieldDeclarationSyntax Dim newField = fieldDeclaration. WithDeclarators(listOfNewModifiedDeclarators) 'Replace the old declaration with the new one Dim newRoot As SyntaxNode = root.ReplaceNode(fieldDeclaration, newField) 'Generate a new document Dim newDocument = document.WithSyntaxRoot(newRoot) 'Return the new document Return newDocument End Function 'Return a new identifier with an 'uppercase letter Private Function _ ConvertName(oldName As String) As String Return Char. ToUpperInvariant(oldName(0)) & oldName.Substring(1) End Function |
As you can see, you have re-used skills you acquired in Chapter 4 to create new syntax elements for updating a FieldDeclaration syntax node. One new thing you saw was how to create instances of collections that are specific to Roslyn (such as SeparatedSyntaxList) without using the SyntaxFactory class.
You test and debug a code refactoring exactly as you do with code analyzers, so you just need to make sure the .vsix project is selected as the startup project, and then press F5. The code refactoring library will be deployed to the experimental instance of Visual Studio, and will be ready for your tests. Of course, you can definitely use the debugging instrumentation you already know. Figure 37 demonstrates how the custom refactoring works over a field declaration in Visual Basic (you get the same result in C#).

Figure 37: Testing a code refactoring
Custom code refactorings definitely improve the coding experience, not only because they allow reorganizing portions of code a better way, but also because they can simplify the implementation of programming patterns. As an example, imagine you want to offer a refactoring that automates the generation of the RelayCommand and ViewModelBase classes in an implementation of the Model-View-ViewModel pattern. You could create a code refactoring that rewrites the syntax tree of a class by adding the syntax nodes required for the pattern implementation.
Tip: You can take a look at an open-source project I created and published on GitHub, called DotNetAnalyzers. Among others, this project has code refactorings in both VB and C# that automate the generation of classes that implement the MVVM pattern.
Refactoring code means reorganizing portions of code a better way without changing the original behavior. In this chapter, you saw how to create a custom code refactoring that integrates with Visual Studio’s quick actions, you saw how to use the Syntax Visualizer to determine the syntax nodes you need to edit, you learned about how a code refactoring is architected, and you tried out the basics for implementing the refactoring logic. Finally, you saw how to test and debug a code refactoring locally. So far, you have learned many concepts about the .NET Compiler Platform, and now you have the skills you need to create code analyzers and refactorings on your own. However, you have only used the result of your work on your own, locally. The big benefit of Roslyn is that code analyzers and refactorings can be shared with other developers. This is discussed in the next two chapters.