Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Clean Code with C#

You're reading from   Clean Code with C# Refactor your legacy C# code base and improve application performance using best practices

Arrow left icon
Product type Paperback
Published in Dec 2023
Publisher Packt
ISBN-13 9781837635191
Length 492 pages
Edition 2nd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Jason Alls Jason Alls
Author Profile Icon Jason Alls
Jason Alls
Arrow right icon
View More author details
Toc

Table of Contents (18) Chapters Close

Preface 1. Chapter 1: Coding Standards and Principles in C# 2. Chapter 2: Code Review – Process and Importance FREE CHAPTER 3. Chapter 3: Classes, Objects, and Data Structures 4. Chapter 4: Writing Clean Functions 5. Chapter 5: Exception Handling 6. Chapter 6: Unit Testing 7. Chapter 7: Designing and Developing APIs 8. Chapter 8: Addressing Cross-Cutting Concerns 9. Chapter 9: AOP with PostSharp 10. Chapter 10: Using Tools to Improve Code Quality 11. Chapter 11: Refactoring C# Code 12. Chapter 12: Functional Programming 13. Chapter 13: Cross-Platform Application Development with MAUI 14. Chapter 14: Microservices 15. Assessments 16. Index 17. Other Books You May Enjoy

FP examples

The only thing that sets FP aside from other methods of programming is that functions do not modify data or state. You will use FP in scenarios such as deep learning (DL), machine learning (ML), and artificial intelligence (AI) when it is necessary to perform different sets of operations on the same set of data.

The LINQ syntax within .NET Framework is an example of FP. So, if you are wondering what FP looks like, and if you have used LINQ before, then you have been subjected to FP and should know what it looks like.

Since FP is a deep subject and many books, courses, and videos exist on this topic, we will only touch on the topic briefly in this chapter by looking at pure functions and immutable data.

A pure function is restricted to only operating on the data that is passed into it. As a result, the method is predictable and avoids producing side effects. This benefits programmers because such methods are easier to reason about and test.

Once an immutable data object or data structure has been initialized, the contained data values will not be modified. Because the data is only set and not modified, you can easily reason about what the data is, how it is set, and what the outcome of any operation will be, given the inputs. Immutable data is also easier to test as you know what your inputs are and what outputs are expected. This makes writing test cases much easier as you don’t have so many things to consider, such as object state. The benefit of immutable objects and structures is that they are thread-safe. Thread-safe objects and structures make for good DTOs that can be passed between threads.

But structs can still be mutable if they contain reference types. One way around this would be to make the reference type immutable. C# 7.2 added support for readonly struct and ImmutableStruct. So, even if our structures contain reference types, we can now use these new C# 7.2 constructs to make structures with reference types immutable.

Now, let’s have a look at a pure function example. The only way to set the properties of an object is via the constructor at construction time. The class is a Player class whose only job is to hold the name of the player and their high score. A method is provided that updates the player’s high score:

public class Player {
    public string PlayerName { get; } 
    public long HighScore { get; } 
    public Player(string playerName, long highScore) 
    {
        PlayerName = playerName; 
        HighScore = highScore; 
    }
    Public Player UpdateHighScore(long highScore) 
    {
        return new Player(PlayerName, highScore); 
    }
}

Notice that the UpdateHighScore method does not update the HighScore property. Instead, it instantiates and returns a new Player class by passing in the PlayerName variable, which is already set in the class, and highScore, which is the method parameter. You have now seen a very simple example of how to program your software without changing its state.

Note

FP is a very large subject and requires a mind shift that can be very difficult for both procedural and OO programmers. Since it is outside the scope of this book (to delve deep into the topic of FP), you are actively encouraged to peruse the FP resources on offer from Packt Publishing for yourself.

Packt has some very good books and videos that specialize in teaching the top tiers of FP. You will find links to some Packt FP resources at the end of this chapter, in the Further reading section.

Before we move on, we will look at some LINQ examples since LINQ is an example of FP in C#. It will be good to have an example dataset. The following code builds a list of vendors and products. We’ll start by writing the Product structure:

public struct Product {
    public string Vendor { get; } 
    public string ProductName { get; } 
    public Product(string vendor, string productName) 
    {
        Vendor = vendor; 
        ProductName = productName; 
    }
}

Now that we have our struct, we will add some sample data inside the GetProducts() method:

public static List<Product> GetProducts(){
    return new List<Product>
    {
        new Product("Microsoft", "Microsoft Office"), 
        new Product("Oracle", "Oracle Database"), 
        new Product("IBM", "IBM DB2 Express"), 
        new Product("IBM", "IBM DB2 Express"), 
        new Product("Microsoft", "SQL Server 2017 Express"), 
        new Product("Microsoft", "Visual Studio 2019 Community Edition"), 
        new Product("Oracle", "Oracle JDeveloper"), 
        new Product("Microsoft", "Azure"), 
        new Product("Microsoft", "Azure"), 
        new Product("Microsoft", "Azure Stack"), 
        new Product("Google", "Google Cloud Platform"), 
        new Product("Amazon", "Amazon Web Services") 
    }; 
}

Finally, we can start to use LINQ on our list. In the preceding example, we got a distinct list of products and ordered by the vendors’ names. Now we will print out the results:

class Program {
    static void Main(string[] args) 
    {
        var vendors = (from p in GetProducts() 
        select p.Vendor) 
        .Distinct() 
        .OrderBy(x => x); 
        foreach(var vendor in vendors) 
        Console.WriteLine(vendor); 
        Console.ReadKey(); 
    }
}

In the provided C# code, the LINQ statements are used to retrieve a distinct list of vendor names from a collection of products and then order them alphabetically. The LINQ statements can be considered pure functions in the context of this code because they do not have any side effects, and their output is solely determined by their input. Here’s an explanation of how these LINQ statements act as pure functions:

  • from p in GetProducts() select p.Vendor: This LINQ statement queries the GetProducts() method to retrieve the Vendor property from each product. It transforms the input collection of products into a new sequence of vendor names. This transformation is a pure function because it does not modify the input collection or have any side effects.
  • .Distinct(): The Distinct() method filters the sequence of vendor names to ensure that each vendor name appears only once in the output. This operation is also a pure function as it does not alter the original sequence and produces a new sequence based on the distinct values.
  • .OrderBy(x => x): The OrderBy method sorts the vendor names alphabetically. This sorting operation is deterministic and does not modify the input sequence but produces a new ordered sequence. It, too, is a pure function.

Overall, the LINQ statements in this code are pure functions because they take an input collection, apply various transformations to it, and produce a new collection without causing any side effects or altering the original data. This functional style of programming is one of the benefits of using LINQ in C#, as it promotes immutability and helps ensure code clarity and maintainability.

In C#, you can create an immutable type by using the record keyword. Here’s an example of a simple immutable record a person:

public record Person{
    public string FirstName { get; set; }
    public string LastName { get; set;}
    public int Age { get; set;}
    public Person(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
    }
}

In this example, the Person record has three properties: FirstName, LastName, and Age, and it has a constructor to initialize these properties. The properties are read-only, meaning you can’t change their values after the record is created.

Here’s how you can use the Person record:

Person person1 = new Person("John", "Doe", 30);Person person2 = new Person("Jane", "Smith", 25);
Console.WriteLine(person1.FirstName); // Output: John
Console.WriteLine(person2.Age);      // Output: 25

Because the Person record is immutable, you can’t modify its properties directly. If you want to create a new Person instance with different values, you would create a new object:

Person updatedPerson = person1 with { Age = 35 };

This creates a new Person record with the same FirstName and LastName properties as person1 but with an updated Age property.

One of the benefits of FP is that your methods are much smaller than the methods in other types of programming. Next, we will take a look at why it is good to keep methods small, as well as the techniques we can use, including FP.

lock icon The rest of the chapter is locked
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.
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 $19.99/month. Cancel anytime
Banner background image