Extending List Functionality with C# Extension Methods

I will demonstrate how to create an extension method in C# for the List type. I will explain how we can extend the functionality of List without a new derived class. I will create extension methods for list types to help us use them across projects without creating a new class.

Let's create an extension method for the list that will help us use it in the project for any list object. Here, I have created five extension methods with examples. I will explain each with examples of how we can use them in our real-time project.

Extension Method Example

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

namespace MyFirstAppInVisualStudio2022.ExtensionMethod
{
    public static class ListExtensions
    {
        public static bool SafeAny<T>(this IList<T> list)
        {
            return list != null && list.Any();
        }

        public static IEnumerable<T> DistinctByProperty<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
        {
            return items.GroupBy(property).Select(x => x.First());
        }

        public static bool HasOneItem<T>(this IList<T> list)
        {
            return list.SafeAny() && list.Count() == 1;
        }

        public static bool HasManyItems<T>(this IList<T> list)
        {
            return list.SafeAny() && list.Count() > 1;
        }

        public static bool HasAny<T>(this IList<T> list, Func<T, bool> predicate)
        {
            return list.SafeAny() && list.Any(predicate);
        }
    }
}

Let's create a class and a few records for that class so we can understand how we can use the above methods for our project.

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public int Age { get; set; }
    public int Experience { get; set; }
}

Now create a list with the null assignment and try to use Any() on it.

public static void Main(string[] args)
{
    List<Employee> employees = null;

    if (employees.Any())
    {
        Console.WriteLine("List has employee");
    }
    int i = 0;
}

// Method to create sample data for employee list
private static void GetEmployeeData(List<Employee> employees)
{
    employees.Add(new Employee()
    {
        Id = 1,
        Name = "Jay",
        Age = 25,
        Experience = 2
    });

    employees.Add(new Employee()
    {
        Id = 1,
        Name = "Yash",
        Age = 30,
        Experience = 5
    });


    employees.Add(new Employee()
    {
        Id = 1,
        Name = "Raj",
        Age = 60,
        Experience = 20
    });

    employees.Add(new Employee()
    {
        Id = 1,
        Name = "Jose",
        Age = 30,
        Experience = 5
    });


    employees.Add(new Employee()
    {
        Id = 1,
        Name = "Preet",
        Age = 60,
        Experience = 20
    });
}

It will throw an error. "System.ArgumentNullException: 'Value cannot be null. (Parameter'source')'" as below,

Will show the following Error

Extension Method. SafeAny()

public static bool SafeAny<T>(this IList<T> list)
{
   return list != null && list.Any();
}

How to use it in a project?

SafeAny method

Impact of this method on code

If we use this extension method in a project, then we will make sure it will not throw an error even if the developer creates a list and initializes it with a null value.

Now we can use SafeAny() to check the null check as well. If the developer assigns a list with null values, then it will not throw any errors, but it will handle null and return false. It will handle unintentional errors in the application.

Unintentional errors

Extension method. DistinctByProperty()

This method will do distinct work on multiple fields; here, I have used the age and experience fields to do distinct work based on two fields.

public static IEnumerable<T> DistinctByProperty<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
   return items.GroupBy(property).Select(x => x.First());
}

How to use it in a project?

public static void Main(string[] args)
{
    List<Employee> employees = new List<Employee>();
    GetEmployeeData(employees);
    
    // Distinct list using multiple fields Age and Experience
    var empList = employees.DistinctByProperty(x => new { x.Age, x.Experience }).ToList();
}

Impact of this method on code

By default, lists do not support distinct values for multiple columns before .NET 6. If you are using older than.NET 6, then you can create an extension method that you can use in the entire project with a simplified version like above.It makes the code cleaner and easier to use when we want to create a distinct list based on multiple columns.

Extension method. HasOneItem()

This method will return true or false. If the list contains only a single value then will return true else false.

public static bool HasOneItem<T>(this IList<T> list)
{
    return list.SafeAny() && list.Count() == 1;
}

How to use it in a project?

public static void Main(string[] args)
{
    List<Employee> employees = new List<Employee>();
    GetEmployeeData(employees);

    //HasOneItem() will return true if the list has only one value.
    if(employees.HasOneItem())
    {
        // This section runs if the employee list has only one item.
    }
}

Impact of this method on code

This method is very useful for checking the count of a list. Using this, you don't need to repeat count() > 0 code everywhere in your project; you can use the HasOneItem() method in the entire project if you create an extension method, which will simplify your code and make it more readable.

Extension method. HasAny(YourCondition) 

HasAny method. we can pass a predicate, and it will return true if the specified condition is satisfied.

public static bool HasAny<T>(this IList<T> list, Func<T, bool> predicate)
{
    return list.SafeAny() && list.Any(predicate);
}

How to use it in a project?

public static void Main(string[] args)
{
    List<Employee> employees = new List<Employee>();
    GetEmployeeData(employees);

    // HasAny method we can pass predicate and it will return true if specified condition setified  

    // below condition will return false as we do not have any employee age greater then 60
    if (employees.HasAny(x => x.Age > 60))
    {
        // this section runs if employees list have any employee age greater then 60
    }

    // below condition will return true as we have employees age greater then 25
    if (employees.HasAny(x => x.Age > 25))
    {
        // this section runs if employees list have any employee age greater then 25
    }
}

Conclusion

In this article, we have learned an extension method for a list object that will help us extend an existing list type, write less code, and make it more readable. In the above, we have created five extension methods for list object types that make our code more robust and maintainable. Extension methods are widely used in practice to promote clean and modular code design.


Similar Articles