3

大まかに言えば、私が作成しているプログラムには、分類可能な少数のエントリ (おそらく常に 30 未満) の格納が含まれます。これらのエントリを表示できるようにしたいのですが、それらを使用してクラスの外部から変更されないようにしたいと考えています。変更可能な Entry というクラスと、Entry オブジェクトのラッパーである ReadOnlyEntry という別のクラスを作成しました。これらの Entry オブジェクトを整理する最も簡単な方法はList<List<Entry>>、それぞれList<Entry>がカテゴリである を作成することです。しかし、そのデータを読み取り専用で公開することは、面倒で複雑になりました。次の各タイプのオブジェクトを 1 つずつ用意する必要があることに気付きました。

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 つ目ではエントリを変更でき、2 つ目ではエントリを追加または削除でき、3 つ目ではカテゴリを追加または削除できます。データが変更されるたびに、これらのラッパーを正確に保つ必要があります。これは私には複雑に思えるので、これを処理する露骨に良い方法があるかどうか疑問に思っています。

編集 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);
}
4

3 に答える 3

4

を使用List<T>.AsReadOnly()して戻ることができますReadOnlyCollection<T>

List<T>また、データをそのまま格納しているクラスを苦しめています。個々のリストを格納するクラスの独自の階層を構築します。

于 2016-03-01T19:18:58.997 に答える
2

.NETコレクションcovarianceをサポートする必要がありますが、コレクション自体はサポートしていません (代わりに、一部のインターフェイスは covariance https://msdn.microsoft.com/ru-ru/library/dd233059.aspxをサポートしています)。共分散は、がのサブクラスである場合、List<Conctrete>のサブクラスのように動作することを意味します。インターフェイスの共変を使用するか、次のようにキャストを使用できます。List<Base>ConcreteBase

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に感謝します。

于 2016-03-01T19:54:08.633 に答える
1

Entryゲッターのみを含むインターフェイスを作成できます。このインターフェイスを介して要素を公開し、読み取り専用アクセスを提供します。

public interface IEntry
{
    int Value { get; }
}

書き込み可能な実装は次のようになります。

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

これで、余分な作業をしなくてもa を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; }
}
于 2016-03-01T19:38:57.040 に答える