Convert IEnumerable to ReadOnlyCollection in C#

Understanding IEnumerable and ReadOnlyCollection in C#

When working with collections in C#, developers frequently utilize IEnumerable<T> to represent a sequence of objects. However, there are scenarios where exposing collection data in a read-only format is crucial to prevent accidental modifications. This is where the ReadOnlyCollection<T> class becomes invaluable, providing a wrapper around an existing collection to ensure consumers can read data without altering it.

In this article, we will explore effective methods for converting an IEnumerable collection into a ReadOnlyCollection in C#, highlighting the benefits and best practices for implementing this approach.

What is IEnumerable?

IEnumerable<T> is one of the most widely used interfaces in the .NET ecosystem. It represents a collection that can be iterated over using constructs like the foreach loop.

Example:

IEnumerable<string> employees = new List<string>
{
    "John",
    "David",
    "Sarah"
};

While IEnumerable<T> allows for enumeration, it does not inherently provide read-only protection for the underlying collection.

What is ReadOnlyCollection?

ReadOnlyCollection<T> is a powerful wrapper class designed to prevent modifications to the items within a collection. Its key benefits include:

The ReadOnlyCollection<T> class resides in the System.Collections.ObjectModel namespace.

Methods for Converting IEnumerable to ReadOnlyCollection

Method 1: Using ToList() and the ReadOnlyCollection Constructor

This is a straightforward and commonly adopted method. You first convert the IEnumerable to a List<T> using the ToList() LINQ extension method, and then pass this list to the ReadOnlyCollection<T> constructor.

Example:

using System.Collections.ObjectModel;
using System.Linq;

IEnumerable<string> employees = new List<string>
{
    "John",
    "David",
    "Sarah"
};

ReadOnlyCollection<string> readOnlyEmployees = 
    new ReadOnlyCollection<string>(employees.ToList());

The output will be:

John
David
Sarah

Method 2: Using List<T>.AsReadOnly()

The List<T> class provides a convenient AsReadOnly() method that directly returns a read-only view of the list. This approach is concise and highly readable.

Example:

using System.Collections.Generic;
using System.Collections.ObjectModel;

IEnumerable<int> numbers = new List<int>
{
    10, 
    20, 
    30
};

ReadOnlyCollection<int> readOnlyNumbers = numbers.ToList().AsReadOnly();

The output will be:

10
20
30

Method 3: Creating a Custom Extension Method

For enhanced code reusability across multiple projects, you can create a custom extension method. This method encapsulates the conversion logic, making it easily accessible wherever you need it.

Example Extension Method:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

public static class CollectionExtensions
{
    public static ReadOnlyCollection<T> ToReadOnlyCollection<T>(
        this IEnumerable<T> source)
    {
        return new ReadOnlyCollection<T>(source.ToList());
    }
}

Usage:

var readOnlyEmployees = employees.ToReadOnlyCollection();

This promotes cleaner code and reduces duplication.

Real-Time Example: Repository Pattern

Consider a scenario where you are retrieving employee data from a repository. To ensure that the data returned by the repository cannot be modified by the calling code, you can convert it to a ReadOnlyCollection.

Repository Method:

public IEnumerable<Employee> GetEmployees()
{
    // Assume _employees is a collection of Employee objects
    return _employees;
}

Converting to ReadOnlyCollection:

ReadOnlyCollection<Employee> employees = 
    GetEmployees()
    .ToList()
    .AsReadOnly();

This guarantees that consumers of the GetEmployees method cannot directly modify the employee records.

Complete Code Example

Here's a full example demonstrating the conversion and usage:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

class Program
{
    static void Main()
    {
        IEnumerable<string> products = new List<string>
        {
            "Laptop",
            "Mouse",
            "Keyboard"
        };

        ReadOnlyCollection<string> readOnlyProducts = products.ToList().AsReadOnly();

        Console.WriteLine("Products:");
        foreach (var item in readOnlyProducts)
        {
            Console.WriteLine(item);
        }
    }
}

The output will be:

Products:
Laptop
Mouse
Keyboard

Benefits of Using ReadOnlyCollection

Best Practices

Common Use Cases

IEnumerable vs. ReadOnlyCollection

Feature IEnumerable<T> ReadOnlyCollection<T>
Enumeration Yes Yes
Read Access Yes Yes
Write Access Depends (on underlying collection) No
Data Protection No Yes
Encapsulation Limited Strong

Conclusion

Converting an IEnumerable to a ReadOnlyCollection is a straightforward yet powerful technique in C#. It empowers developers to expose collection data safely, preventing unintended modifications and enhancing application reliability. By employing methods like ToList(), AsReadOnly(), or custom extension methods, you can easily create immutable collection views.

For enterprise applications, the repository pattern, APIs, and service layers, ReadOnlyCollection offers an excellent mechanism to enforce data integrity and maintain a clean, robust architecture.