Search code examples
c#covariancereadonly-collection

Read only list of lists c#


In general terms, a program I'm making involves storing a small number of entries (probably less than 30 at any given time) which can be categorized. I want to allow these entries to be seen but not altered from outside the class using them. I made a class called Entry which could be modified and another called ReadOnlyEntry which is a wrapper for an Entry object. The easiest way to organize these Entry objects it seems is to create a List<List<Entry>>, where each List<Entry> is a category. But then exposing that data in a readonly way became messy and complicated. I realized I would have to have one object of each of the following types:

List<List<Entry>> data;
List<List<ReadOnlyEntry>>  //  Where each ReadOnlyEntry is a wrapper for the Entry in the same list and at the same index as its Entry object.
List<IReadOnlyCollection<ReadOnlyEntry>>  //  Where each IReadOnlyCollection is a wrapper for the List<ReadOnlyEntry> at the same index in data.
IReadOnlyCollection<IReadOnlyCollection<ReadOnlyList>> readOnlyList  //  Which is a wrapper for the first item I listed.

The last item in the list would be exposed as public. The first lets me change entries, the second lets me add or delete entries, and the third lets me add or delete categories. I would have to keep these wrappers accurate whenever the data changes. This seems convoluted to me, so I'm wondering if there's a blatantly better way to handle this.

Edit 1: To clarify, I know how to use List.asReadOnly(), and the stuff I proposed doing above will solve my problem. I'm just interested in hearing a better solution. Let me give you some code.

class Database
{
    //  Everything I described above takes place here.

    //  The data will be readable by this property:
    public IReadOnlyCollection<IReadOnlyCollection<ReadOnlyList>> Data
    {
        get
        {
            return readOnlyList;
        }
    }

    //  These methods will be used to modify the data.
    public void AddEntry(stuff);
    public void DeleteEntry(index);
    public void MoveEntry(to another category);
    public void AddCategory(stuff);
    public void DeleteCategory(index);
}

Solution

  • .NET collections should support covariance, but they don't support it themselves (instead some interfaces support covariance https://msdn.microsoft.com/ru-ru/library/dd233059.aspx). Covariance means List<Conctrete> behaves like subclass of List<Base> if Concrete is subclass of Base. You can use interfaces covariation or just use casting like this:

    using System.Collections.Generic;
    
    namespace MyApp
    {
        interface IEntry
        {
        }
    
        class Entry : IEntry
        {
        }
    
        class Program
        {
            private List<List<Entry>> _matrix = null;
    
            public List<List<IEntry>> MatrixWithROElements
            {
                get 
                {
                    return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
                }
            }
    
            public IReadOnlyList<List<IEntry>> MatrixWithRONumberOfRows
            {
                get 
                {
                    return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
                }
            }
    
            public List<IReadOnlyList<IEntry>> MatrixWithRONumberOfColumns
            {
                get 
                {
                    return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry) as IReadOnlyList<IEntry>);
                }
            }
    
            public IReadOnlyList<IReadOnlyList<IEntry>> MatrixWithRONumberOfRowsAndColumns
            {
                get 
                {
                    return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
                }
            }
    
            public void Main(string[] args)
            {    
            }
        }
    }
    

    Thanks to Matthew Watson for pointing on errors in my previous answer version.