How to expose Collections in a Class?

One of the most fundamental questions that I often think about is how to expose a collection in a class to other classes.

There are lot of ways of to do it. In this blog I would like to show what people are generally doing and what are pros and cons or each approach in my view.

Finally, I would like to identify my preferred way and why?

Here are the ways people are generally exposing their collection.

  1. Expose Collection as Public property.
    1. public class FileData  
    2. {  
    3. public IList<string> ColumnNames { getset; }  
    4. }  
    Pros

    The biggest advantage is its simplicity.

    Cons

    The consumer of this Property can add or remove items from the collection.

    Although, many people may not find a problem with the con I have suggested, but in my view this is a big problem.

    Consider a hypothetical scenario:

    Suppose this property is being used by all other classes. They are adding and removing elements from this collection. Now, a new rule comes up that only the column name starting with “c_” should be allowed to add in the collection, otherwise an exception should be thrown. Where will this logic be implemented? Keeping the encapsulation and DRY(Do Not Repeat yourself) in mind the only logical place is FileData class only. Once, the method is added then change need to be done at all other classes to use the new method.

    Therefore, I do not prefer to expose my collection to the outside world as it is. It should be exposed as a read only collection to the outside world, and specific public methods should be provided for adding and removing the elements.
     
  2. In order to expose the collection as a read only collection, I have some seen some people doing this as well.
    1. public class FileData  
    2. {  
    3. private IList<string> _columnNames = new List<string>();  
    4. public IEnumerable ColumnNames  
    5. {  
    6. get { return _columnNames; }  
    7. }  
    8. public void AddColumnName(string columnName)  
    9. {  
    10. _columnNames.Add(columnName);  
    11. }  
    12. // Remove Column method---  
    13. }  
    Notice

    That the return type of the property is now IEnumerable in order to make the collection read only. Also, separate methods have been created for adding and removing elements from collection.

    Pros

    Provides separate method for adding and removing items from the collection.

    Cons

    Although, it seems that by making the return type of the collection as IEnumerable, user will not able to add elements to the collection but it’s not the case.

    Other classes can typecast the ColumnNames as IList and then add new column name.
    1. var fileData = new FileData();  
    2. var collection = (IList<string>) fileData.ColumnNames;  
    3. collection.Add("My New Entry");  
    This approach suffers from the same deficiency as described in option 1.

    The third approach is of using ReadOnlyCollection. (If one is using C#4.5 and above, Interfaces IReadOnlyList or IReadOnlyCollection are returned.)


    1. public class FileData  
    2. {  
    3. private IList<string> _columnNames = new List<string>();  
    4. public ReadOnlyCollection<string> ColumnNames  
    5. {  
    6. get { return new ReadOnlyCollection<string>(_columnNames); }  
    7. }  
    8. public void AddColumnName(string columnName)  
    9. {  
    10. _columnNames.Add(columnName);  
    11. }  
    12. }  
    Pros

    The collection is read only and there are separate methods for Add and Remove.

    Cons

    A concrete class of ReadOnlyCollection is being returned instead of Interface.

What is my preferred way of implementation.

Looking at all the options, I prefer to use option 3. Although, a concrete class is being returned, but this is a problem only in C# 4.0. In C# 4.5 there are interfaces IReadOnlyList, IReadOnlyCollection which can be used as return types. So for now, I will use the concrete class (ReadOnlyCollection) as return type but will move to IReadOnlyList or IReadOnlyCollection in C# 4.5.

Also, from the performance stand point creating new object of ReadOnlyCollection is not that expensive. It is a O(1) operation.