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 Sr. Software Developer at Kunz, Leigh, & Associates in Okemos, MI. 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 vogelvision@gmail.com.

comments powered by Disqus

Reader Comments:

Thu, Sep 13, 2012 Tom

Is there a way to cast/mark a read/write collection as read only after the collection is built? This is a common scenario when providing a collection to a calling method. Other languages would let you mark parameters as constant or entire methods as constant.

Tue, Sep 11, 2012 II ARROWS Italy

I'd like to know if there are significant gain in performance... But I don't think so.

Tue, Sep 11, 2012 Jenda

(I hate pages without comment preview!)It ate all the generic type parameters ... let me try again with HTML entities: Well ... John ... because it can't. First, it can't protect the data from modification ... you may not want to allow whoever you are passing the data to add or remove items to and from the dictionary. Second, those nasty words covariance and contravariance are important. Try to pass a Dictionary<string,Dog> to a method expecting a Dictionary<string,Animal>! The thing is that if the structure is readonly it's fine if you treat its values as Animals even if they are all Dogs. If it's mutable, you can't. The method might try to add a Lizzard into the dictionary which is fine for Dictionary<string,Animal>, but not for Dictionary<string,Dog>.

Tue, Sep 11, 2012 Jenda

Well ... John ... because it can't. First, it can't protect the data from modification ... you may not want to allow whoever you are passing the data to add or remove items to and from the dictionary. Second, those nasty words covariance and contravariance are important. Try to pass a Dictionary to a method expecting a Dictionary! The thing is that if the structure is readonly it's fine if you treat its values as Animals even if they are all Dogs. If it's mutable, you can't. The method might try to add a Lizzard into the dictionary which is fine for Dictionary, but not for Dictionary.

Tue, Sep 11, 2012 Wouter

Will there be implementations of these interfaces? When used as you listed you can cast the readonly interfaces to the original type and modify them. I guess this feature is more about suggestion of use instead of security.

Thu, Aug 23, 2012 John

Hi Eric, Why would I use a IReadOnlyDictionary when I can use Dictionary? I mean Dictionary can do whatever IReadOnlyDictionary can and more so why bother with the read only version?

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.