Refactoring with C#

Book description

Learn how to improve the maintainability of C# code using modern tools and techniques to resolve technical debt while working safely and efficiently with legacy code

Key Features

  • Apply a wide range of refactoring techniques using the latest tools and features of C#
  • Discover ways to safely improve your code using tests, Roslyn analyzers, and AI assistance
  • Find ways of communicating technical debt and improving your code in agile and enterprise settings
  • Purchase of the print or Kindle book includes a free PDF eBook

Book Description

Software projects start as brand-new greenfield projects, but invariably become muddied in technical debt far sooner than you’d expect. In Refactoring with C#, you'll explore what technical debt is and how it arises before walking through the process of safely refactoring C# code using modern tooling in Visual Studio and more recent C# language features using C# 12 and .NET 8. This book, written by a Microsoft MVP, will guide you through the process of refactoring safely through advanced unit testing with XUnit and libraries like Moq, Snapper, and Scientist .NET. You'll explore maintainable code through SOLID principles and defensive coding techniques made possible in newer versions of C#. You'll also find out how to run code analysis and write custom Roslyn analyzers to detect and resolve issues unique to your code.

The nature of coding is changing, and you'll explore how to use AI with the GitHub Copilot Chat to refactor, test, document, and generate code before ending with a discussion about communicating technical debt to leadership and getting organizational buy-in to refactor your code in enterprise organizations and in agile teams.

By the end of this book, you'll understand the nature of refactoring and see how you can safely, effectively, and repeatably pay down the technical debt in your application while adding value to your business.

What you will learn

  • Understand technical debt, its causes and effects, and ways to prevent it
  • Explore different ways of refactoring classes, methods, and lines of code
  • Discover how to write effective unit tests supported by libraries such as Moq
  • Understand SOLID principles and factors that lead to maintainable code
  • Use AI to analyze, improve, and test code with the GitHub Copilot Chat
  • Apply code analysis and custom Roslyn analyzers to ensure that code stays clean
  • Communicate tech debt and code standards successfully in agile teams

Who this book is for

This book is for any developer familiar with C# who wants to improve the code they work with on a day-to-day basis. While this book will be most beneficial to new developers with only a year or two of experience, even senior engineers and engineering managers can make the most of this book by exploring not just the process of refactoring, but advanced techniques with libraries like Moq, Snapper, Scientist .NET, and writing custom Roslyn analyzers.

Table of contents

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

Product information

  • Title: Refactoring with C#
  • Author(s): Matt Eland
  • Release date: November 2023
  • Publisher(s): Packt Publishing
  • ISBN: 9781835089989