Reader small image

You're reading from  Refactoring with C#

Product typeBook
Published inNov 2023
Reading LevelIntermediate
PublisherPackt
ISBN-139781835089989
Edition1st Edition
Languages
Right arrow
Author (1)
Matt Eland
Matt Eland
author image
Matt Eland

Matt Eland is a Microsoft MVP in Artificial Intelligence (AI) who has been working with .NET since 2001. Matt has served as a senior engineer, software engineering manager, and .NET programming instructor. He is currently an AI specialist and senior consultant at Leading EDJE near Columbus, Ohio, where he helps companies with their software engineering and data science needs using C# and related technologies. Matt speaks and writes in his community and co-organizes the Central Ohio .NET Developers Group while pursuing a master's degree in data analytics.
Read more about Matt Eland

Right arrow

Refactoring Code Flow and Iteration

While other chapters in Part 1 focus on refactorings that can be applied to entire methods or classes, this chapter focuses on improving the readability and efficiency of individual lines of code.

Developers spend the majority of their time reading over individual lines of code and only a fraction of that time modifying code. So, it is important to make our lines of code as maintainable as possible.

In this chapter, we’ll explore the following topics related to improving small pieces of code:

  • Controlling program flow
  • Instantiating objects
  • Iterating over collections
  • Refactoring LINQ statements
  • Reviewing and testing our refactored code

Technical requirements

The starting code for this chapter is available from GitHub at https://github.com/PacktPublishing/Refactoring-with-CSharp in the Chapter03/Ch3BeginningCode folder.

Refactoring the boarding app

This chapter’s code focuses on a pair of applications for Cloudy Skies Airline:

  • A Boarding Status Display app that tells the user if it’s time for them to board their flight based on the current boarding group and the person’s ticket, military status, and whether or not they need assistance getting down the jetway.
  • A Boarding Kiosk app that allows airline employees to view the passengers scheduled to be on the flight and provides information regarding whether each passenger has boarded. Figure 3.1 shows the application in action:
Figure 3.1 – The Boarding Kiosk app

Figure 3.1 – The Boarding Kiosk app

Since we’re exploring not one but two applications, we’ll meet the application code in small chunks as we progress through this chapter. However, feel free to peruse it yourself on GitHub if you’d like to orient yourself first.

As we go through this chapter, we’ll take its existing functioning...

Controlling program flow

One of the most basic things new developers learn is how programs execute lines of code in sequence and how if statements and other language features control what statements execute next.

In this section, we’ll focus on the BoardingProcessor class's CanPassengerBoard method. The method starts simple enough:

public string CanPassengerBoard(Passenger passenger) {
  bool isMilitary = passenger.IsMilitary;
  bool needsHelp = passenger.NeedsHelp;
  int group = passenger.BoardingGroup;

Here, CanPassengerBoard takes in a Passenger object and returns a string. The method also declares a few local variables holding pieces of data from the object passed in.

These variables aren’t necessary and could be removed by performing an inline variable refactoring, which we’ll talk about later in this chapter. However, as they improve the readability of the code that follows, their existence is largely helpful...

Instantiating objects

Now that we’ve sufficiently improved our CanPassengerBoard method, let’s look at how we can create objects and see a few simple improvements you can make that will simplify object instantiation in your code.

Terminology notes

New developers are often tripped up by a handful of phrases that are commonly used by developers. For example, in this section, we will talk about instantiating objects. This is a common way of phrasing this for developers, but all it means is the process of creating a specific instance of a class using the new keyword. When you see the term instantiating, you can think of it simply as creating a specific instance of something.

This section’s code could come from anywhere, but we’ll focus on code found in a pair of methods in the PassengerTests.cs file in the test project that accompanies this chapter.

Replacing var with explicit Types

The first line of code I want to focus on comes from one of our...

Iterating over collections

To start exploring collections, let’s go back to the BoardingProcessor class and look at its DisplayPassengerBoardingStatus method. We’ll explore this method a bit at a time, starting with its method signature:

public void DisplayBoardingStatus(
  List<Passenger> passengers, bool? hasBoarded = null) {

Here, we can see that the method takes in a list of Passenger objects and, optionally, a nullable boolean hasBoarded parameter that can store true, false, or null. This hasBoarded parameter is used to optionally filter down our list of passengers based on its value:

  • true: Only include passengers who have boarded the plane
  • false: Only include passengers who have not yet boarded
  • null: Do not filter by boarded status (default option)

This nullable filtering parameter is a common one I see while building search methods and we’ll explore it in more depth again in Chapter 5, Object-oriented Refactoring...

Refactoring LINQ statements

In this final section of this chapter, we’ll review a few of the more common optimizations with LINQ code by focusing on some common improvements most codebases that use LINQ will benefit from.

Choosing the correct LINQ method

LINQ has several different ways of finding a specific item in a collection.

If you had an IEnumerable<Passenger> interface named people and wanted to find someone by their name, you might write code like this:

LinqExamples.cs

PassengerGenerator generator = new();
List<Passenger> people = generator.GeneratePassengers(50);
Passenger me =
  people.FirstOrDefault(p => p.FullName == "Matt Eland");
Console.WriteLine($"Matt is in group {me.BoardingGroup}");

This code uses the LINQ FirstOrDefault method, which searches the collection until it finds the first value that the arrow function evaluates as true. In this example, it’d find the first person with FullName...

Reviewing and testing our refactored code

While we didn’t modify a lot of code in this chapter, the code we did change shrunk in size, thus becoming easier to read, understand, and modify in the process.

This is why we refactor. Refactoring should actively improve the maintainability of our applications and pay down strategic pieces of technical debt that threaten to introduce bugs and delays in the future.

Refactored code

The final refactored code from this chapter is available in the https://github.com/PacktPublishing/Refactoring-with-Csharp repository inside the Chapter03/Ch3RefactoredCode folder.

Since the art of refactoring involves changing the form of code without changing its functionality, we must test the application before moving on.

We’ll talk more about manual and automated tests in Chapter 6, but for now, run the tests by selecting the Test menu at the top of Visual Studio and then clicking Run All Tests.

This will show Test Explorer and...

Summary

In this chapter, we explored refactoring techniques to help better control program flow, instantiate objects, iterate over collections, and write more efficient code through LINQ.

Each refactoring technique we covered is one tool in your toolbelt that might improve the readability and maintainability of your code in the right circumstances. As you practice refactoring more, you’ll learn more about when to apply which refactoring to improve the code you’re working with.

In the next chapter, we’ll move on from improving individual lines of code and focus on a slightly bigger picture as we work to refactor entire methods of C# code.

Questions

Answer the following questions to test your knowledge of this chapter:

  1. Is it more important to have concise code or readable code?
  2. Scroll through a file of code in a project you are working on. What do you notice about the if statements in your code?
  3. How frequently are nested if statements used?
  4. Is any logic repeated frequently in conditions of your if statements?
  5. Do you see any places where inverting the if statement or switching to a switch statement or switch expression could improve things?
  6. Do you think your team has been using LINQ to its fullest potential when working with collections? What opportunities for improvement do you see?

Further reading

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Refactoring with C#
Published in: Nov 2023Publisher: PacktISBN-13: 9781835089989
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Matt Eland

Matt Eland is a Microsoft MVP in Artificial Intelligence (AI) who has been working with .NET since 2001. Matt has served as a senior engineer, software engineering manager, and .NET programming instructor. He is currently an AI specialist and senior consultant at Leading EDJE near Columbus, Ohio, where he helps companies with their software engineering and data science needs using C# and related technologies. Matt speaks and writes in his community and co-organizes the Central Ohio .NET Developers Group while pursuing a master's degree in data analytics.
Read more about Matt Eland