Discover the Power of C# Extension Methods

Introduction

Extension methods provide the ability to incorporate additional methods into existing types without the need to create a new derived type, recompile the code, or make any alterations to the original type. These methods are static in nature, yet they can be invoked as if they were instance methods belonging to the extended type. When it comes to client code written in C#, F#, and Visual Basic, there is no discernible distinction between invoking an extension method and utilizing the methods defined within a type.

Extension methods offer a solution for expanding the capabilities of a class without access to the source code or authorization to modify the class in the future. Prior to the introduction of extension methods, inheritance was commonly utilized to add new members to an existing class without altering the original class by creating a child class and defining the new members within it.

Benefits of Extension Methods

  • Enhanced Code Reusability: By incorporating common functionality into types without altering their original source code or resorting to inheritance.
  • Improved Readability: Enhancing the readability of code by enabling methods to be invoked on the objects they are applied to.
  • Simplified Maintainability: Facilitating the management of enhancements and utilities independently from the types they are extending.
  • Effective Encapsulation: Ensuring that the extension logic remains encapsulated within static classes.

Extension Method Guidelines

  • Effective Namespace Management: It is crucial to place extension methods in a well-defined namespace to facilitate their discoverability without cluttering the global namespace.
  • Exercise Restraint: It is advisable to use extension methods sparingly to maintain code readability. They should be employed judiciously.
  • Prevent Conflicts: Take caution when creating extension methods to avoid any potential conflicts with existing or future methods of the extended type.
  • Thorough Documentation: Given that extension methods may not be as self-evident as instance methods, it is essential to document them adequately.

Points to Keep in Mind when working with C# Extension methods

  1. Extension methods should only be defined within a static class.
  2. It is worth noting that a Static Class in C# consists solely of Static Members. Since an extension method is defined within a static class, it implies that the extension method should be created as a static method.
  3. The first parameter of an extension method is referred to as the binding parameter, which should be the name of the class to which the method is to be bound. The binding parameter should be prefixed with 'this'.
  4. An extension method can only have one binding parameter, which should be specified at the beginning of the parameter list.
  5. If necessary, an extension method can also be defined with regular parameters starting from the second position in the parameter list.

1. Types of Extension Methods

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpTestingArticles
{
    public static class StringExtensionsExample
    {
        // 1. String Extensions: Enhancing the string type by incorporating additional functions for string manipulation, validation, and formatting.
        public static string ToUpperCaseTitle(this string str)
        {
            return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str);
        }

        // 2. Collection Extensions: Collection Extensions involve the addition of methods to IEnumerable<T>, List<T>, or other collection types in order to streamline operations like filtering, mapping, or aggregation.
        public static IEnumerable<T> CheckWhereNotNullValue<T>(this IEnumerable<T?> source) where T : class
        {
            return source.Where(x => x != null);
        }

        // 3. Extensions for numbers: Implementing functions to numeric types for standard mathematical calculations or formatting.
        public static double ConvertToRadians(this double degrees)
        {
            return (Math.PI / 180) * degrees;
        }

        // 4. DateTime Extensions: Enhancing DateTime or DateTimeOffset with additional methods to streamline date manipulations.
        public static DateTime GetStartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
        {
            int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
            return dt.AddDays(-1 * diff).Date;
        }

        // 5. Custom Type Extensions: Custom Type Extensions involve the addition of methods to custom types in your application in order to encapsulate commonly used logic.
        public static void Deconstruct(this KeyValuePair<string, int> kvp, out string key, out int value)
        {
            key = kvp.Key;
            value = kvp.Value;
        }
    }
}

2. Extension Methods with parameters

In C#, extension methods can accept additional parameters in addition to the one that defines the extended type. This feature enables the inclusion of more versatile and practical methods to existing types.

A). Extension methods with "out" parameters.

public static void Deconstruct(this KeyValuePair<string, int> kvp, out string key, out int value)
{
    key = kvp.Key;
    value = kvp.Value;
}

B). Extension method with parameter

public static class ExtensionMethodWithParameter
{
    public static string SetRepeatedValue(this string str, int count)
    {
        if (count < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be negative.");
        }

        StringBuilder sb = new StringBuilder(str.Length * count);
        for (int i = 0; i < count; i++)
        {
            sb.Append(str);
        }
        return sb.ToString();
    }
}

C). Extension method with return type

public static class ExtensionsMethodWithParameter
{
    public static List<string> SplitIntoChunksProcess(this string str, int chunkSize)
    {
        if (chunkSize <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(chunkSize), "Chunk size must be greater than zero.");
        }

        List<string> chunks = new List<string>();
        for (int i = 0; i < str.Length; i += chunkSize)
        {
            int length = Math.Min(chunkSize, str.Length - i);
            chunks.Add(str.Substring(i, length));
        }

        return chunks;
    }
}

3. Enum Type Extension Method

public enum Grades { F = 0, D = 1, C = 2, B = 3, A = 4 };

public static class EnumTypeExtensions
{
    public static Grades minPassing = Grades.D;

    public static bool Passing(this Grades grade)
    {
        return grade >= minPassing;
    }
}

4. Testing Result

In Visual Studio, when you type a dot (.) after an instance of an object, the IntelliSense feature shows a list of available methods and properties. Extension methods are typically indicated with a specific icon (a downward arrow) to differentiate them from instance methods.

Extension methods

using System.Windows;

namespace ExtensionMethodsExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // String extension method with parameter
            string text = "HelloWorld";
            int chunkSize = 3;
            List<string> chunks = text.SplitIntoChunksProcess(chunkSize);

            foreach (string chunk in chunks)
            {
                Console.WriteLine(chunk);
            }

            // Enum extension method
            var result = EnumTypeExtensions.Passing(Grades.A);
        }
    }
}