2009-03-06 11 views
19

ich die folgende Klasse:„Nur Lesen“ haben Eigenschaftenaccessor in C#

class SampleClass 
{ 
    private ArrayList mMyList; 

    SampleClass() 
    { 
     // Initialize mMyList 
    } 

    public ArrayList MyList 
    { 
     get { return mMyList;} 
    } 
} 

ich Benutzer möchte in der Lage sein mMyList zu bekommen, weshalb ich jedoch die „get“ über eine Eigenschaft ausgesetzt i nicht möchte, dass sie Änderungen am Objekt vornehmen (zB. MyList.Add (new Class());), um wieder in meine Klasse zu gelangen.

Ich denke, ich kann eine Kopie des Objekts zurückgeben, aber das kann langsam sein und ich bin auf der Suche nach einem Weg, der einen Fehler bei der Kompilierung liefert, der den Benutzer informiert, dass er nicht erwarten kann, das zu ändern Rückgabewert von der Eigenschaft.

Ist das möglich?

+0

Verwandte Frage zu stellen: http://stackoverflow.com/questions/681287/how-to-make-a-reference-type-property-readonly –

+2

Hat 'List : ReadOnlyCollection ' in .NET 4.5 irgendwelche davon ändern? http://www.infoq.com/news/2011/10/ReadOnly-WInRT/ –

Antwort

25

Mit einer ArrayList sind Sie ziemlich eingeschränkt, da es in der BCL keine schreibgeschützte Sammelklasse gibt. Die schnelle und schmutzige Lösung besteht darin, einen Typ von IEnumerable zurückzugeben.

public IEnumerable MyList 
    { 
     get { return mMyList;} 
    } 

Dies wird nicht wirklich jemand aus Gießen verhindern zu Arraylist, aber es wird keine Änderungen standardmäßig erlauben entweder

Sie können eine effektive Nur-Lese-Liste zurückkehren, indem ArrayList.ReadOnly aufrufen. Allerdings ist der Rückgabetyp eine ArrayList, so dass der Benutzer trotzdem mit .Add kompilieren könnte, aber es würde einen Laufzeitfehler erzeugen.

1

Sie sollten den Rückgabewert in eine schreibgeschützte Auflistung einschließen.

19

Verwenden Sie die ArrayList.ReadOnly()-Methode zum Erstellen und Zurückgeben eines schreibgeschützten Wrappers um die Liste. Es wird die Liste nicht kopieren, sondern nur einen schreibgeschützten Wrapper um sie herum erstellen. Um die Kompilierzeitüberprüfung zu erhalten, möchten Sie wahrscheinlich den schreibgeschützten Wrapper als IEnumerable zurückgeben, wie es @ Jared vorschlägt.

public IEnumerable MyList 
{ 
    get { return ArrayList.ReadOnly(mMyList); } 
} 

Eine Kollektion, die nur gelesen wird, ist einfach eine Sammlung mit einer Umhüllung, die die Sammlung Modifizierung verhindert. Wenn Änderungen an der zugrundeliegenden Sammlung vorgenommen werden, spiegelt die schreibgeschützte Sammlung diese Änderungen wider.

Diese Methode ist eine O (1) -Operation.

Reference

+2

Ich bin gegen diese Lösung, weil es einen Kompilierzeitfehler in einen Laufzeitfehler verwandelt. – JaredPar

+0

-1, OP: "Ich bin auf der Suche nach einem Weg, der eine ** Kompilierzeit ** Fehler" –

+0

Ohne Kopieren in eine ReadOnlyCollection - die er auch sagte, dass er vermeiden wollte - ich wähle die Durchsetzung Lese- nur über vorgeben, nur gelesen zu werden. Doing beides ist eigentlich wahrscheinlich besser. – tvanfosson

-3

einfach die Eigenschaft machen, wie versiegelt (oder NotInheritable in VB.NET) und Nur-Lese, wie folgt aus:

public sealed readonly ArrayList MyList 
    { 
     get { return mMyList;} 
    } 

Vergessen Sie auch nicht als Nur-Lese das dahinter liegende Feld zu machen :

private readonly ArrayList mMyList; 

Aber um den Wert von mMyList zu initialisieren, müssen Sie es NUR im Konstruktor initialisieren, a s in diesem Beispiel:

public SampleClass() 
{ 
    // Initialize mMyList 
    mMyList = new ArrayList(); 
} 
10

Nur um JaredPar's Antwort zu erweitern. Wie er sagte, ist das Zurückgeben des tatsächlichen Hintergrundfelds nicht vollständig sicher, da der Benutzer das IEnumerable weiterhin dynamisch zu einem ArrayList zurückgeworfen werden kann.

würde ich so etwas schreiben keine Änderungen an der ursprünglichen Liste, um sicherzustellen, werden gemacht:

public IEnumerable MyList 
{ 
    get 
    { 
     foreach (object o in mMyList) 
      yield return o; 
    } 
} 

Dann wieder, würde ich probabily auch eine generische Liste verwenden (IEnumerable<T>) vollständig sicher geben werden.

+0

* Dies ist der richtige - Wunsch, ich könnte zweimal wählen. Irgendein Problem mit dem Aufruf von 'yield break;' nach der Schleife? – Jay

+0

@Jay: Es gibt keine Notwendigkeit für eine explizite 'Ausbeute Pause;' im Anschluss an die Schleife. Das Ende der Methode impliziert das Ende des'Enumerator' (und 'MoveNext' wird korrekt' false' zurückgeben). –

+0

Mit dieser Lösung verliert der Benutzer nicht die Funktionalität von ArrayList wie schnellen Direktzugriff und schnelle Zählung? –

4

Verwenden Sie eine ReadOnlyCollection.

return new ReadOnlyCollection<object>(mMyList).

Sie können auch das Feld ReadOnlyCollection ein Feld erstellen, das automatisch Änderungen in der zugrunde liegenden Liste widerspiegelt. Dies ist die effizienteste Lösung.

Wenn Sie wissen, dass mMyList nur eine Art von Objekt enthält (zB, hat es nichts anderes als Datetime), Sie eine ReadOnlyCollection<DateTime> (oder eine andere Art) zurückkehren kann für zusätzliche Typsicherheit. Wenn Sie C# 3 verwenden, können Sie einfach schreiben

return new ReadOnlyCollection<DateTime>(mMyList.OfType<DateTime>().ToArray())

Dies wird jedoch nicht automatisch mit der zugrunde liegenden Liste aktualisieren, und sind auch weniger effizient (Es wird die gesamte Liste kopieren) . Die beste Möglichkeit ist mMyList eine generische List<T> (zB ein List<DateTime>)

0

Verfügbar ab .NET v2.0

public ArrayList MyList { get; private set; } 
+0

Sie können immer noch Add in einer vorhandenen Liste verwenden. –