2009-07-03 10 views
3

Ich hätte gerne eine Klasse "A" mit einer (zum Beispiel) SortedList Sammlung "SrtdLst" -Eigenschaft, und innerhalb dieser Klasse "A" erlauben die Addition oder Subtraktion von "SrtdLst" -Elementen. Aber in einer Instanz der Klasse "A", nur erlauben, den Inhalt der Elemente zu erhalten oder zu setzen, nicht neue Elemente hinzuzufügen oder die bestehenden zu subtrahieren. In Code:C# Wie macht man öffentliche Getter und Setter und private Methoden für eine Sammlung?

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 Member "DerivativeParameters" verwenden, das eine Sammlung von "Parameters" füllt, sodass nur der Wert jedes Elements geändert werden kann.

Wie kann das gemacht werden?

+5

Bitte sagen Sie mir, dass Sie nicht No-Vowels-erlaubt Notation auf einer täglichen Basis verwenden! Ich nehme an, es war nur für dieses Beispiel. Ich beschäftige mich den ganzen Tag mit Notation bei der Arbeit, die ich "einen Vokal erlaubt, aber es ist nicht immer der erste!"; Es ist schrecklich zu lesen. – jcollum

+1

Ja, einverstanden, aber es ist ein Freitag Nacht Post ... – Alex

Antwort

7

Wenn Wenn Sie die Änderungsoperationen zum Zeitpunkt der Kompilierung sperren möchten, benötigen Sie eine typsichere Lösung.

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

Dann deklarieren Sie eine Klasse, die diese Schnittstelle implementiert und von der Standardauflistungsklasse erbt.

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

Vorausgesetzt, dass Sie die Schnittstellendefinition richtig, werden Sie nichts von Hand implementieren müssen, da die Basisklasse bereits die Implementierungen bietet.

Verwenden Sie diese abgeleitete Klasse als den Typ des Felds, in dem der Eigenschaftswert gespeichert wird.

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 können nur die Untermenge von Operationen verwenden, die über IReadOnlyList<T> verfügbar sind.

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

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

ich es IEnumerable auch erben gemacht habe sein muss, die nur lesbar ist sowieso, so ist vollkommen sicher . Die sichere Klasse wäre dann:

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

Aber sonst ist es die gleiche Idee.

Update: gerade bemerkt, dass (aus irgendeinem Grund kann ich nicht ergründen) Sie nicht modifizierende Operationen zu verbieten wollen - Sie wollen nur einige modifizierende Operationen zu verbieten. Sehr seltsam, aber es ist immer noch die selbe Lösung. Was auch immer Operationen, die Sie zulassen wollen, „öffnen sie nach 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 wollen, über Add Hinzufügen verbieten, aber nicht verbieten es über den Indexer? (Der Indexer verwendet werden können Artikel hinzufügen, genauso wie die Add-Methode kann.)

aktualisieren

Von Ihrem Kommentar Ich glaube, Sie bedeuten, dass Sie Zuordnung auf den Wert eines vorhandenen Schlüssels zulassen möchten/Wertpaar, aber Zuweisung zu einem zuvor unbekannten Schlüssel nicht zulassen. Da Schlüssel zur Laufzeit durch Strings angegeben werden, gibt es keine Möglichkeit, das zur Kompilierungszeit zu erfassen. 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 
} 

Jedes Mal, wenn ein gewöhnliches Wörterbuch haben, und Sie möchten ein bestimmtes Verfahren geben Sie es, dass Sie nicht vertrauen, die vorhandenen Werte zu aktualisieren, wickeln Sie es in einem der diese.

+0

Ich möchte alle verbannen, aber ich dachte, den Indexer zu verbieten, um Elemente über Setter hinzuzufügen, aber ich wusste nicht, wie man den Zusatz durch die Methode "Add" verbietet. Wie ich in meinem vorherigen Kommentar zu Skeet gesagt habe, möchte ich eine Klasse wie System.Data.SqlClient.SqlCommand für die Stored Procedures erstellen, mit einem Member wie "DeriveParameters", der eine Sammlung von "Parameters" füllt, also nur die Wert jedes Artikels kann geändert werden. – Alex

+0

Siehe weitere Aktualisierung. –

+0

FYI .NET 4.5 haben genau diese Schnittstellen IReadOnlyList und IReadOnlyDictionary

6

EDIT: Ursprüngliche Antwort ist unten. Wie oarwicker hervorhebt, habe ich nicht bemerkt, dass Sie nicht fragen, dass es readonly - nur um den Add Betrieb zu verhindern. Das klingt für mich nicht nach einer guten Idee, da der einzige Unterschied zwischen Add und dem Indexer-Setter darin besteht, dass Add eine Ausnahme auslöst, wenn das Element bereits vorhanden ist. Das könnte der Anrufer sowieso leicht fälschen.

Warum möchten Sie nur diese eine Operation einschränken?


Ursprüngliche Antwort

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

Offenbar möchten Sie eine schreibgeschützte Wrapper-Klasse um eine beliebige IDictionary. Sie können dann eine öffentliche Eigenschaft haben, die den Wrapper zurückgibt, während Sie innerhalb Ihrer Klasse auf die private Variable 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 ...

+0

Verdammt Sie und Ihre Omnipräsenz Skeet: P – annakata

+0

Ich weiß! Bekommen Sie einen Job Skeet! : P – jcollum

+0

Ich mag es nicht, Fehler zur Laufzeit zu erkennen, daher verwendet meine Antwort stattdessen das statische System. Aber das andere Problem (das habe ich auch nicht bemerkt!) Ist, dass das OP nicht darum bittet, es nur zu lesen. Sie möchten nur die Add-Methode verbieten und erlauben dennoch die Zuweisung via Indexer (die auch Elemente hinzufügen kann). Sehr merkwürdig ... –

0

Wenn die Schlüssel außerhalb der Klasse bekannt sind, können Sie der Wrapper-Klasse ein ChangeItem (Schlüssel, newValue) und ReadItem (Schlüssel) hinzufügen. Dann halten Sie die SortedList privat für die Klasse.

2

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 zuzugreifen den Index:

a["KeyA"] = "ValueBBB"; 

Da jedoch die Indexer der Liste ermöglicht die Erstellung neuer Elemente, Sie müssten Code in den Indexer hinzufügen, um zu verhindern, dass, wenn Sie nicht möchten, dass dies möglich ist.