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

Defensive Coding Techniques

Code is almost organic and evolves over its lifetime as new features are added, fixes are implemented, and refactorings occur at regular intervals. As code changes and developers enter and leave the project, there’s a chance that some of these changes may introduce bugs.

In Part 2 of this book, we discussed testing strategies for detecting these bugs before they reach production. In this chapter, we’ll talk about a few additional techniques that help developers catch and resolve bugs during development. Along the way, we’ll also explore a few newer features in C# and their roles in keeping your code stable and healthy.

We’ll cover the following topics in this chapter:

  • Validating inputs
  • Protecting against null
  • Moving beyond classes
  • Advanced type usage

Technical requirements

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

The code in this chapter talks to a REST API, which will require an active internet connection.

Introducing the Cloudy Skies API

Our fictitious sample organization, Cloudy Skies, has a pre-existing set of web services in the form of a public REST API. This API intends to allow interested organizations to pull information about Cloudy Skies flights through the API. However, a steady amount of support tickets has proven that organizations are having a hard time adopting the API and using it in approved ways.

In response, Cloudy Skies has built a .NET library to help others more easily use the API.

Early testing of this library is promising, but some developers are still encountering confusing errors that ultimately appear to be related to the data they’re passing the library.

The development team decided that validating parameters to public methods would help improve the adoption of their library by finding issues sooner. We’ll explore this change in the next section.

Validating inputs

Input validation is the act of verifying that any inputs to your code, such as parameters or current property values, are correct before performing the requested work. We validate inputs to public methods to detect potential issues early on.

To illustrate the importance of this, let’s look at a method that doesn’t validate its inputs:

public FlightInfo? GetFlight(string id, string apiKey) {
  RestRequest request = new($"/flights/{id.ToLower()}");
  request.AddHeader("x-api-key", apiKey);
  LogApiCall(request.Resource);
  return _client.Get<FlightInfo?>(request);
}

The GetFlight method takes in an id parameter indicating a flight number, such as “CSA1234,” whereas the apiKey parameter represents a token that must be supplied to interact with the API and get a response. Think of the token as something like a digital keycard that Cloudy Skies issues to interested organizations...

Protecting against null

British computer scientist, Tony Hoare, is generally credited as the inventor of the null reference in programming. In 2008, he famously apologized for it, calling it his “billion-dollar mistake.” This was due to the countless bugs and crashes that have occurred in various programming languages when code attempted to interact with variables currently holding null values. While I can’t fault Tony Hoare, nulls can certainly be dangerous.

In .NET, this comes in the form of a NullReferenceException error, as we saw earlier in this chapter. You get a NullReferenceException error any time you attempt to invoke a method or evaluate a property on a variable that currently holds a null value.

Before C# 8, developers needed to be explicitly aware that any reference type could hold a null value and write conditional logic, such as the following code:

if (flight != null) {
 Console.WriteLine($"Flight {flight.Id}: {flight.Status}"...

Moving beyond classes

In C# 9 and beyond, Microsoft has made concerted efforts to give developers new options for working with classes through things such as record types, init-only properties, primary constructors, and more.

In this section, we’ll explore how these newer C# constructs can improve the design of your classes.

Preferring immutable classes

In recent years, immutable classes have become more and more popular. This immutability refers to the inability to change an object after it has been created.

What this means is that once an object exists, you cannot modify its state and instead are limited to creating new objects that are like the original. If you’re familiar with working with string and DateTime objects in .NET, you’ve seen this concept with methods such as ToLower on string and AddDays on DateTime returning a new object instead of modifying the original object.

Let’s look at a small class representing a boarding pass that...

Advanced type usage

In this final section of this chapter, we’ll see how new and old language features help you build better types.

Exploring pattern matching

It turns out that we can use the same style of syntax we used with expressions earlier to conditionally match different objects through pattern matching.

To explain what I mean, let’s start with an example that loops over different boarding passes:

List<BoardingPass> passes = PassGenerator.Generate();
foreach (BoardingPass pass in passes) {
  if (pass is { Group: 1 or 2 or 3,
                Flight.Status: FlightStatus.Pending
              }) {
    Console.WriteLine($"{pass.Passenger} board now");
  } else if (pass is { Flight.Status: FlightStatus.Active
       ...

Summary

In this chapter, we looked at a variety of ways of ensuring your classes are safe and reusable through means such as argument validation, caller member information, nullability analysis, and using modern C# features such as record classes, primary constructors, pattern matching, and enhanced properties with the required and init keywords.

These language features help you detect issues earlier in development, work with objects more effectively, and write fewer lines of code overall.

This concludes Part 2 of this book. In Part 3, we’ll look at how AI and code analysis tools can help you and your team sustainably build better software.

Questions

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

  1. How can throwing exceptions be beneficial to your code?
  2. What are the various ways you can declare a property in C#?
  3. What are the various ways you can instantiate an object in C#?
  4. What are the differences between classes and record classes?

Further reading

You can find more information about features discussed in this chapter at these URLs:

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 €14.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