C# コレクションのパブリック ゲッターとセッター、およびプライベート メソッドを作成するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/1080644

  •  22-08-2019
  •  | 
  •  

質問

(たとえば) SortedList コレクションの "SrtdLst" プロパティを持つクラス "A" を持ち、このクラス "A" 内で "SrtdLst" 項目の加算または減算を許可したいと考えています。ただし、クラス「A」のインスタンスでは、項目のコンテンツの取得または設定のみが許可され、新しい項目の追加や既存の項目の削除は許可されません。コード内:

class A
{
    public SortedList<string, string> SrtdLst = new SortedList<string, string>();

    public A()
    {
        // This must work:
        SrtdLst.Add("KeyA", "ValueA");
        // This too:
        SrtdLst["KeyA"] = "ValueAAA";
    }
}

class B
{
    public A a = new A();
    public B()
    {
        // I want the following code to fail:
        a.SrtdLst.Add("KeyB", "ValueB");
        // But this must work:
        a.SrtdLst["KeyA"] = "ValueBBB";
   }
}

アップデート:System.Data.SqlClient.SqlCommand のようなクラスを作成したいと考えています。ストアド プロシージャの場合、「パラメータ」のコレクションを埋めるメンバー「DeriveParameters」を使用できるため、各項目の値のみを変更できます。

これはどうすればできるのでしょうか?

役に立ちましたか?

解決

コンパイル時に変更操作を禁止したい場合は、タイプセーフなソリューションが必要です。

公的に許可された操作のインターフェースを宣言します。そのインターフェイスをプロパティ タイプとして使用します。

public interface IReadOnlyList<T>
{
    T this[int index] { get; }

    int Count { get; }
}

次に、そのインターフェイスを実装し、標準のコレクション クラスを継承するクラスを宣言します。

public class SafeList<T> : List<T>, IReadOnlyList<T> { }

インターフェイス定義を正しく理解していると仮定すると、基本クラスがすでに実装を提供しているため、何も手動で実装する必要はありません。

その派生クラスを、プロパティ値を格納するフィールドの型として使用します。

public class A
{
    private SafeList<string> _list = new SafeList<string>();

    public IReadOnlyList<string>
    {
        get { return _list; }
    }
}

クラス A 内では、次を使用できます。 _list 直接実行するので、内容を変更します。クラス A のクライアントは、次の方法で利用できる操作のサブセットのみを使用できます。 IReadOnlyList<T>.

あなたの例では、List の代わりに SortedList を使用しているため、インターフェイスはおそらく次のようにする必要があります。

public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
    V this[K index] { get; }        
}

IEnumerable も継承するようにしましたが、いずれにせよ読み取り専用なので完全に安全です。安全なクラスは次のようになります。

public class SafeSortedList<K, V> : SortedList<K, V>, IReadOnlyDictionary<K, V> { }

しかし、それ以外の場合は同じ考えです。

アップデート:(何らかの理由で理解できませんが) 変更操作を禁止したいのではなく、一部の変更操作を禁止したいだけであることに気づきました。非常に奇妙ですが、それでも同じ解決策です。どのような操作を許可したい場合でも、インターフェースで「操作を開きます」:

public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
    V this[K index] { get; set; }        
}

もちろん、それは今のインターフェースの間違った名前です...一体なぜ、Add による追加は禁止したいのに、インデクサーによる追加は禁止しないのでしょうか?(Add メソッドと同様に、インデクサーを使用して項目を追加できます。)

アップデート

コメントから、既存のキー/値ペアの値への割り当ては許可するが、以前に不明なキーへの割り当ては許可しないということを意味していると思います。明らかに、キーは実行時に文字列によって指定されるため、コンパイル時にそれをキャッチする方法はありません。したがって、実行時チェックを行うこともできます。

public class FixedSizeDictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
    IDictionary<TKey, TValue> _realDictionary;

    public FixedSizeDictionaryWrapper(IDictionary<TKey, TValue> realDictionary)
    {
        _realDictionary = realDictionary;
    }

    public TValue this[TKey key]
    {
        get { return _realDictionary[key]; }

        set 
        {
            if (!_realDictionary.Contains(key))
                throw new InvalidOperationException();

            _realDictionary[key] = value;
        }
    }

    // Implement Add so it always throws InvalidOperationException

    // implement all other dictionary methods to forward onto _realDictionary
}

通常のディクショナリがあり、それを既存の値の更新を信頼できないメソッドに渡したい場合は、それをこれらのいずれかでラップします。

他のヒント

編集:元の答えは以下です。ハサミムシが指摘したように、私はあなたがいることに気づきませんでした そうではありません 読み取り専用にするよう要求します - 単にそれを防ぐためです Add 手術。それは私には良い考えとは思えません。 Add インデクサーセッターは Add 要素がすでに存在する場合は例外をスローします。いずれにせよ、これは発信者によって簡単に偽装される可能性があります。

なぜその 1 つの操作だけを制限したいのですか?


元の回答

まず、パブリックフィールドは使用しないでください。それは問題に遭遇する確実な方法です。

任意のラッパークラスを囲む読み取り専用のラッパークラスが必要なようです IDictionary. 。これにより、クラス内からプライベート変数にアクセスしながら、ラッパーを返すパブリック プロパティを持つことができます。例えば:

class A
{
    private SortedList<string, string> sortedList = new SortedList<string, string>();

    public IDictionary<string, string> SortedList 
    {
        get { return new ReadOnlyDictionaryWrapper(sortedList);
    }

    public A()
    {
        sortedList.Add("KeyA", "ValueA");
        sortedList["KeyA"] = "ValueAAA";
    }
}

あとは、 ReadOnlyDictionary 実装...今は実装できませんが、必要に応じて後で戻ってきます...

ただ、リストをプライベートに、インデクサとして公開:

class A {

   private SortedList<string, string> _list;

   public A() {
      _list = new SortedList<string, string>()
   }

   public string this[string key] {
      get {
         return _list[key];
      }
      set {
         _list[key] = value;
      }
   }

}

これであなただけのインデックスを使用してアイテムにアクセスすることができます:

a["KeyA"] = "ValueBBB";
リストのインデクサーが新しいアイテムを作成することができますように、

しかし、あなたがしたくない場合はそれが可能かということを防止するためにインデクサーにコードを追加する必要があります。

キーは、クラスの外で知られている場合は、

あなたはあなたのラッパークラスにChangeItem(キー、newValueに)とReadItem(キー)を追加することができます。そして、クラスにSortedListののプライベートを保ちます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top