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

  • IDE Irony: Coding Errors Cause 'Critical' Vulnerability in Visual Studio

    In a larger-than-normal Patch Tuesday, Microsoft warned of a "critical" vulnerability in Visual Studio that should be fixed immediately if automatic patching isn't enabled, ironically caused by coding errors.

  • Building Blazor Applications

    A trio of Blazor experts will conduct a full-day workshop for devs to learn everything about the tech a a March developer conference in Las Vegas keynoted by Microsoft execs and featuring many Microsoft devs.

  • Gradient Boosting Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the gradient boosting regression technique, where the goal is to predict a single numeric value. Compared to existing library implementations of gradient boosting regression, a from-scratch implementation allows much easier customization and integration with other .NET systems.

  • Microsoft Execs to Tackle AI and Cloud in Dev Conference Keynotes

    AI unsurprisingly is all over keynotes that Microsoft execs will helm to kick off the Visual Studio Live! developer conference in Las Vegas, March 10-14, which the company described as "a must-attend event."

  • Copilot Agentic AI Dev Environment Opens Up to All

    Microsoft removed waitlist restrictions for some of its most advanced GenAI tech, Copilot Workspace, recently made available as a technical preview.

Subscribe on YouTube