列表 C# 的只读列表

本文关键字:列表 只读 | 更新日期: 2023-09-27 18:36:59

一般来说,我正在制作的程序涉及存储少量可以分类的条目(在任何给定时间可能少于30个)。我想允许看到这些条目,但不能从使用它们的类外部进行更改。我创建了一个名为Entry的类,可以修改,另一个名为ReadOnlyEntry的类是Entry对象的包装器。组织这些 Entry 对象的最简单方法似乎是创建一个List<List<Entry>>,其中每个List<Entry>都是一个类别。但是,以只读方式公开该数据变得混乱而复杂。我意识到我必须拥有以下每种类型的一个对象:

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.

列表中的最后一项将公开为公共项目。第一个允许我更改条目,第二个允许我添加或删除条目,第三个允许我添加或删除类别。每当数据更改时,我都必须保持这些包装器的准确性。这对我来说似乎很复杂,所以我想知道是否有一种明显更好的方法来解决这个问题。

编辑 1:澄清一下,我知道如何使用List.asReadOnly(),我上面建议做的事情将解决我的问题。我只是有兴趣听到更好的解决方案。让我给你一些代码。

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);
}

列表 C# 的只读列表

您可以使用

List<T>.AsReadOnly()返回ReadOnlyCollection<T>

此外,您正在折磨以您的方式存储数据的List<T>类。 构建自己的类层次结构,用于存储各个列表。

.NET集合应该支持协方差,但它们本身不支持协方差(相反,某些接口支持协方差 https://msdn.microsoft.com/ru-ru/library/dd233059.aspx)。协方差意味着如果List<Conctrete>Base的子类Concrete则表现得像List<Base>的子类。您可以使用接口协变,也可以像这样使用强制转换:

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)
        {    
        }
    }
}

感谢Matthew Watson指出我以前的答案版本中的错误。

您可以为Entry创建一个仅包含 getter 的接口; 您将通过此接口公开元素以提供只读访问权限:

public interface IEntry
{
    int Value { get; }
}

可写实现将很简单:

public sealed class Entry : IEntry
{
    public int Value { get; set; }
}

现在,您可以利用这样一个事实,即您可以将List<List<Entry>>作为IReadOnlyCollection<IReadOnlyCollection<IEntry>>返回,而无需执行任何额外的工作:

public sealed class Database
{
    private readonly List<List<Entry>> _list = new List<List<Entry>>();
    public Database()
    {
        // Create your list of lists.
        List<Entry> innerList = new List<Entry>
        {
            new Entry {Value = 1},
            new Entry {Value = 2}
        };
        _list.Add(innerList);
    }
    public IReadOnlyCollection<IReadOnlyCollection<IEntry>> Data => _list;
}

请注意 Data 属性的实现是多么简单。

如果需要向IEntry添加新属性,则还必须将它们添加到Entry,但不需要更改Database类。

如果您使用的是 C#5 或更早版本,Data将如下所示:

public IReadOnlyCollection<IReadOnlyCollection<IEntry>> Data
{
    get { return _list; }
}