Frage

Ich mag eine Klasse „A“, um mit einer (zum Beispiel) SortedList Sammlung „SrtdLst“ Eigenschaft, und innerhalb dieser Klasse „A“ der Addition oder Subtraktion von „SrtdLst“ Elementen ermöglichen. Aber in einer Instanz der Klasse „A“, erlaubt nur zu erhalten oder den Inhalt der Elemente festgelegt, keine neue Elemente hinzufügen oder die bestehenden subtrahieren. In dem Code ein:

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

UPDATE: Ich möchte eine Klasse wie System.Data.SqlClient.SqlCommand erstellen. Für die Stored Procedures können Sie das Mitglied „DeriveParameters“ verwenden, die eine Sammlung von „Parametern“ füllt, so dass nur der Wert der einzelnen Elemente kann geändert werden.

Wie kann dies geschehen?

War es hilfreich?

Lösung

Wenn Sie die modifizierende Operationen bei der Kompilierung verbieten wollen, müssen Sie eine typsichere Lösung.

Deklarieren Sie eine Schnittstelle für die öffentlich zulässigen Operationen. Verwenden Sie diese Schnittstelle als Objekttyp.

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

    int Count { get; }
}

Dann eine Klasse deklarieren, die diese Schnittstelle implementiert und erbt von der Standard-Collection-Klasse.

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

Angenommen, Sie die Schnittstellendefinition Recht bekommen, brauchen Sie nicht alles von Hand zu implementieren, da die Basisklasse bereits die Implementierungen bietet.

Verwenden Sie die Klasse, die als Typ des Feldes abgeleitet, die den Eigenschaftswert speichert.

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

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

Innerhalb der Klasse A können Sie _list direkt verwenden und so den Inhalt ändern. Clients der Klasse A nur in der Lage sein, die Teilmenge von Operationen über IReadOnlyList<T> zur Verfügung zu verwenden.

Für Ihr Beispiel, Sie verwenden SortedList statt List, so die Schnittstelle wahrscheinlich sein muss

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

Ich habe es gemacht IEnumerable erben als auch, was nur lesbar ist sowieso, so vollkommen sicher ist. Die sichere Klasse wäre dann:

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

Aber ansonsten ist es die gleiche Idee.

Update: habe gerade bemerkt, dass (aus irgendeinem Grund kann ich nicht ergründen) Sie Vorgänge nicht verbieten wollen modifizieren - Sie einige modifizierende Operationen nur verbieten wollen. Sehr seltsam, aber es ist immer noch die gleiche Lösung. Was auch immer gewünschte Operationen ermöglichen „öffnen Sie sie oben“ in der Schnittstelle:

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

Natürlich, das ist der falsche Name für die Schnittstelle jetzt ... warum auf der Erde würden Sie über Add Hinzufügen verbieten wollen, aber es nicht über den Indexer verbieten? (Der Indexer verwendet werden können Artikel hinzufügen, genauso wie die Add-Methode kann.)

Aktualisieren

Von Ihrem Kommentar Ich glaube, Sie meinen, dass Sie den Wert eines vorhandenen Schlüssel / Wert-Paares erlauben die Zuordnung wollen, aber die Zuordnung zu einem bisher unbekannten Schlüssel nicht zulassen. Offensichtlich als Schlüssel zur Laufzeit von Strings angegeben wird, gibt es keine Möglichkeit, dass bei der Kompilierung zu fangen. So man kann auch zur Laufzeitprüfung gehen:

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
}

Sie haben jederzeit ein gewöhnliches Wörterbuch und Sie wollen es zu einem gewissen Methode zur Hand, die Sie nicht vertrauen die vorhandenen Werte zu aktualisieren, wickeln Sie es in einer von diesen.

Andere Tipps

EDIT: Original Antwort ist unten. Wie Earwicker weist darauf hin, hatte ich nicht bemerkt, dass Sie nicht Bitte um es nur lesbar zu sein - nur den Add Betrieb zu verhindern. Das klingt nicht wie eine gute Idee zu mir, als der einzigen Unterschied zwischen Add und dem Indexer-Setter ist, dass Add eine Ausnahme auslöst, wenn das Element bereits vorhanden ist. Das leicht gefälscht sowieso vom Anrufer werden.

Warum wollen Sie nur, dass ein Betrieb einschränken wollen?


Original Antwort

Für eine Sache, verwenden Sie keine öffentlichen Bereichen. Das ist ein absolut sicherer Weg, auf Probleme zu stoßen.

Es sieht aus wie Sie eine Nur-Lese-Wrapper-Klasse runden eine beliebige IDictionary wollen. Sie können dann eine öffentliche Eigenschaft haben, die den Wrapper zurück, während Sie die privaten Variable aus Ihrer Klasse zugreifen. Zum Beispiel:

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

Jetzt müssen Sie nur eine ReadOnlyDictionary Implementierung finden ... Ich kann es nicht implementieren gerade jetzt, aber ich werde später wieder, wenn nötig sein ...

So stellen Sie die Liste privat, und setzen Sie es als Indexer:

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

}

Jetzt können Sie nur die Elemente Zugriff auf den Index mit:

a["KeyA"] = "ValueBBB";

Wie jedoch der Indexer der Liste Schaffung neuer Produkte ermöglicht, würden Sie Code in der Indexer hinzufügen müssen, um zu verhindern, dass, wenn Sie nicht wollen, das möglich zu tun sein.

Wenn die Schlüssel außerhalb der Klasse bekannt sind, dann können Sie eine ChangeItem (Schlüssel, newValue) und ReadItem (Taste), um Ihre Wrapper-Klasse hinzuzufügen. Dann halten Sie die SortedList der Klasse privat.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top