C# Corner

The New Read-Only Collections in .NET 4.5

Eric Vogel covers some practical uses for the long awaited interfaces, IReadOnlyList and IReadOnlyDictionary in .NET Framework 4.5.

The Microsoft .NET Framework 4.5 includes the IReadOnlyList, IReadOnlyDictionary and IReadOnlyCollection generic interfaces. The main benefit is that the new interfaces are covariant, except for IReadOnlyDictionary. This means that you can use a derived type as the generic parameter, when passing in a collection into a method that's defined for a base type. If you have a Dog class, for example, that derives from Animal, you can have a method that accepts an IReadOnlyList<Animal> and pass it an IReadOnlyList<Dog>.

The IReadOnlyCollection interface, which forms the base of the IReadOnlyList and IReadOnlyDictionary classes, is defined as IReadOnlyCollection<out T>. The out modifier was added in .NET 4 to denote covariance, whereas the in modifier marks a type as being contravariant. A contravariant type may be substituted by one of its base classes.

Prior to .NET 4.5, the primary covariant collection interface was IEnumerable<out T>. If you wanted to have a read-only view of a List or a Dictionary class, you had to roll your own custom interface, or class, to get the full feature set. Enough theory, let's look at some real applications of these new interfaces.

A common scenario you may run into, is storing a list of people or employees. The application may be a case or customer relationship management system. Either way, you're dealing with similar class representations. For example, if you have a Person class that contains FirstName and LastName properties (Listing 1), and an Employee subclass that adds EIN and Salary properties (Listing 2). This is a very simplified view of a business domain, but it gets the picture across.

You could then create a typed list of Employee objects and access them as a read-only collection using the new interfaces. In a real-world application, your employee list is likely to be quite large and retrieved from a database.

List<Employee> employees = new List<Employee>()
{
    new Employee() { EIN = 1, FirstName = "John", LastName  = "Doe", Salary= 55000M },
    new Employee() { EIN = 2, FirstName = "Jane", LastName = "Doe", Salary= 55000M },
    new Employee() { EIN = 3, FirstName = "Don", LastName  = "DeLuth", Salary= 55000M },
};

The IReadOnlyCollection is the most basic read-only collection interface and provides a Count property on top of its inherent IEnumerable members. For example, you could store a read-only view of employees for a directory listing and easily retrieve the number of people.

IReadOnlyCollection<Person> directory = employees;
int numStaff = directory.Count;

The IReadOnlyList interface is the same as IReadOnlyCollection with the addition of an item indexer.

  IReadOnlyList<Person> staff = employees;
 Person firstHire = staff[0];

The IReadOnlyList would be well-suited for a read-only grid display of the needed items.

The IReadOnlyDictionary interface, as its name suggests, provides a read-only view of the Dictionary class. The accessible Dictionary class members include the Keys, Values and key indexer properties, in addition to the ContainsKey and TryGetValue methods.

Dictionary<int, Employee> einLookUp = employees.ToDictionary(x => x.EIN);
IReadOnlyDictionary<int, Employee> readOnlyStaff = einLookUp;
var eins = readOnlyStaff.Keys;
var allEmployees = readOnlyStaff.Values;
var secondStaff = readOnlyStaff[2];
bool haveThirdEin = readOnlyStaff.ContainsKey(3);
Employee test;
bool fourthExists = readOnlyStaff.TryGetValue(4, out test);

The IReadOnlyDictionary interface could prove useful for validation, as you would not need to modify the items but may want to quickly access them via a key, such as a control identifier.

As you can see, there are many uses for the new read-only collection interfaces. Primarily, they can be used to clean up your application's API to indicate that a method or class should not modify the contents of a collection that it is accessing. One caveat to note is that the interfaces do not provide an immutable copy of the collection but rather a read-only view of the source mutable collection.

About the Author

Eric Vogel is a Senior Software Developer for Red Cedar Solutions Group in Okemos, Michigan. He is the president of the Greater Lansing User Group for .NET. Eric enjoys learning about software architecture and craftsmanship, and is always looking for ways to create more robust and testable applications. Contact him at [email protected].

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube