Table of contents : Refactoring with C# Foreword Contributors About the author About the reviewers Preface Who this book is for What this book covers To get the most out of this book Download the example code files Conventions used Get in touch Share Your Thoughts Download a free PDF copy of this book Part 1: Refactoring with C# in Visual Studio 1 Technical Debt, Code Smells, and Refactoring Understanding technical debt and legacy code Where technical debt comes from Identifying code smells Introducing refactoring Refactoring tools in Visual Studio Case study – Cloudy Skies Airlines Summary Questions Further reading 2 Introduction to Refactoring Technical requirements Refactoring a baggage price calculator Converting properties to auto properties Introducing locals Introducing constants Introducing parameters Removing unreachable and unused code Extracting methods Refactoring manually Testing refactored code Refactoring in other editors Refactoring in Visual Studio Code with the C# Dev Kit Refactoring in JetBrains Rider Refactoring in Visual Studio with ReSharper Summary Questions Further reading 3 Refactoring Code Flow and Iteration Technical requirements Refactoring the boarding app Controlling program flow Inverting if statements Dropping else statements after return statements Restructuring if statements Using ternary operators Converting if statements into switch statements Converting to switch expressions Instantiating objects Replacing var with explicit Types Simplifying creation with target-typed new Using object initializers Iterating over collections Introducing foreach Converting to for loops Converting to LINQ Refactoring LINQ statements Choosing the correct LINQ method Combining LINQ methods Transforming with Select Reviewing and testing our refactored code Summary Questions Further reading 4 Refactoring at the Method Level Technical requirements Refactoring the flight tracker Refactoring methods Changing method access modifiers Renaming methods and parameters Overloading methods Chaining methods Refactoring constructors Generating constructors Chaining constructors Refactoring parameters Reordering parameters Adding parameters Introducing optional parameters Removing parameters Refactoring to functions Using expression-bodied members Passing functions as parameters with actions Returning data from Actions with Funcs Introducing static methods and extension methods Making methods static Moving static members to another type Creating extension methods Reviewing and testing our refactored code Summary Questions Further reading 5 Object-Oriented Refactoring Technical requirements Refactoring the flight search system Organizing classes via refactoring Moving classes to individual files Renaming files and classes Changing namespaces Avoiding partial classes and regions Refactoring and inheritance Overriding ToString Generating equality methods Extracting a base class Moving interface implementations up the inheritance tree Controlling inheritance with abstract Communicating intent with abstract Introducing abstract members Converting abstract methods to virtual methods Refactoring for better encapsulation Encapsulating fields Wrapping parameters into a class Wrapping properties into a class Favoring composition over inheritance Improving classes with interfaces and polymorphism Extracting interfaces Providing default interface implementations Introducing polymorphism Reviewing and testing our refactored code Summary Questions Further reading Part 2: Refactoring Safely 6 Unit Testing Technical requirements Understanding testing and unit tests Types of tests and the testing pyramid Unit tests Testing code with xUnit Creating an xUnit Test Project Connecting the xUnit Test Project to your main project Writing your first unit test Organizing tests with Arrange/Act/Assert Understanding tests and exceptions Adding additional test methods Refactoring unit tests Parameterizing tests with Theory and InlineData Initializing test code with constructors and fields Sharing test code with methods Exploring other testing frameworks Testing with NUnit Testing with MSTest Adopting a testing mindset Incorporating testing into your workflow Isolating dependencies Evaluating good and bad tests Thoughts on code coverage Summary Questions Further reading 7 Test-Driven Development Technical requirements What is Test-Driven Development? Test-Driven Development with Visual Studio Setting the starting balance Adding miles and generating methods Redeeming miles and refactoring tests When to use Test-Driven Development Summary Questions Further reading 8 Avoiding Code Anti-Patterns with SOLID Identifying anti-patterns in C# code Writing SOLID code Single Responsibility Principle Open-Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle Considering other architectural principles Learning the DRY principle KISS principle Understanding high cohesion and low coupling Summary Questions Further reading 9 Advanced Unit Testing Technical requirements Creating readable tests with Shouldly Installing the Shouldly NuGet package Writing readable assertions with Shouldly Writing readable assertions with FluentAssertions Testing performance with Shouldly Generating test data with Bogus Mocking dependencies with Moq and NSubstitute Understanding the need for mocking libraries Creating mock objects with Moq Programming Moq return values Verifying Moq calls Mocking with NSubstitute Pinning tests with Snapper Experimenting with Scientist .NET Summary Questions Further reading 10 Defensive Coding Techniques Technical requirements Introducing the Cloudy Skies API Validating inputs Performing basic validation Using the nameof keyword Validation with guard clauses Guard clauses with the GuardClauses library Using CallerMemberInformation attributes Protecting against null Enabling nullability analysis in C# Using nullability operators Moving beyond classes Preferring immutable classes Using required and init-only properties Primary constructors Converting classes into record classes Cloning objects using with expressions Advanced type usage Exploring pattern matching Using generics to reduce duplication Introducing type aliases with the using directive Summary Questions Further reading Part 3: Advanced Refactoring with AI and Code Analysis 11 AI-Assisted Refactoring with GitHub Copilot Technical requirements Introducing GitHub Copilot Understanding GitHub’s predictive model Starting the conversation with GitHub Copilot Chat Getting started with GitHub Copilot in Visual Studio Installing and activating GitHub Copilot Getting access to GitHub Copilot Generating suggestions with GitHub Copilot Interacting with GitHub Copilot Chat Refactoring with GitHub Copilot Chat GitHub Copilot Chat as a code reviewer Targeted refactoring with GitHub Copilot Chat Drafting documentation with GitHub Copilot Chat Generating test stubs with GitHub Copilot Chat Understanding the limits of GitHub Copilot Data privacy and GitHub Copilot Concerns around GitHub Copilot and public code Case study: Cloudy Skies Airline Summary Questions Further reading 12 Code Analysis in Visual Studio Technical requirements Calculating code metrics in Visual Studio Performing code analysis in Visual Studio Analyzing your solution using the default ruleset Configuring code analysis rulesets Responding to code analysis rules Treating warnings as errors Exploring advanced code analysis tools Tracking code metrics with SonarCloud and SonarQube In-depth .NET analysis with NDepend Case study – Cloudy Skies Airline Summary Questions Further reading 13 Creating a Roslyn Analyzer Technical requirements Understanding Roslyn Analyzers Installing the extension development workload and DGML editor Introducing Syntax Visualizer Creating a Roslyn Analyzer Adding the analyzer project to our solution Defining a code analysis rule Analyzing symbols with our Roslyn Analyzer Tips for writing Roslyn Analyzers Testing Roslyn Analyzers with RoslynTestKit Adding a Roslyn Analyzer test project Creating AnalyzerTestFixture Verifying that our Roslyn Analyzer doesn’t flag good code Verifying that our Roslyn Analyzer flags bad code Debugging Roslyn Analyzers Sharing analyzers as Visual Studio extensions Creating a Visual Studio extension (VSIX) for your Roslyn Analyzer Summary Questions Further reading 14 Refactoring Code with Roslyn Analyzers Technical requirements Case study – Cloudy Skies Airlines Building a Roslyn Analyzer code fix Creating a CodeFixProvider Registering a code fix Modifying the document with a code fix Testing Code Fixes with RoslynTestKit Publishing Roslyn Analyzers as NuGet packages Understanding NuGet package deployment Building a NuGet package Deploying the NuGet package Referencing the NuGet package Packaging a CodeFixProvider as an extension Summary Questions Further reading Part 4: Refactoring in the Enterprise 15 Communicating Technical Debt Overcoming barriers to refactoring Urgent deadlines “Don’t touch high-risk code” “This code is going away, don’t spend time on it” End-of-life applications “Just do the minimum required” “Refactoring doesn’t provide business value” Communicating technical debt Technical debt as risk Creating a risk register Alternatives to a risk register Prioritizing technical debt Calculating risk priorities with a risk score The “gut feeling” approach Getting organizational buy-in Setting up the conversation Anticipating questions and objections Different approaches for different leaders The importance of communication Case study – Cloudy Skies Airlines Summary Questions Further reading 16 Adopting Code Standards Technical requirements Understanding code standards Establishing code standards Formatting and code cleanup in Visual Studio Applying code standards with EditorConfig Reviewing our starter code Adding an EditorConfig Customizing EditorConfigs Summary Questions Further reading 17 Agile Refactoring Refactoring in an agile environment Key elements of agile teams Understanding obstacles to refactoring Succeeding with agile refactoring strategies Dedicated work items for refactoring efforts Refactoring code as it changes Refactoring sprints Refactoring sabbaticals Accomplishing large-scale refactorings Why large refactorings are difficult The rewrite trap Lessons from the ship of Theseus Upgrading projects with.NET Upgrade Assistant Refactoring and the strangler fig pattern Recovering when refactoring goes wrong The impact of failed refactorings Establishing safety in agile environments Deploying large-scale refactorings Using feature flags Phased rollouts and blue/green deployments The value of continuous integration and continuous delivery Case study – Cloudy Skies Airlines Summary Toward more sustainable software Questions Further reading Index Why subscribe? Other Books You May Enjoy Packt is searching for authors like you Share Your Thoughts Download a free PDF copy of this book