Warum IList oder List verwenden?


82

Ich weiß, dass es viele Beiträge dazu gegeben hat, aber es verwirrt mich immer noch, warum Sie eine Schnittstelle wie IList übergeben und eine Schnittstelle wie IList anstelle der konkreten Liste zurückgeben sollten.

Ich habe viele Beiträge gelesen, in denen es darum geht, die Implementierung später einfacher zu ändern, aber ich sehe einfach nicht ganz, wie das funktioniert.

Sagen Sie, ob ich diese Methode habe

  public class SomeClass
    {
        public bool IsChecked { get; set; }
    }

 public void LogAllChecked(IList<SomeClass> someClasses)
    {
        foreach (var s in someClasses)
        {
            if (s.IsChecked)
            {
                // log 
            }
        }
    }

Ich bin mir nicht sicher, wie mir die Verwendung von IList in Zukunft helfen wird.

Wie wäre es, wenn ich bereits in der Methode bin? Sollte ich IList weiterhin verwenden?

public void LogAllChecked(IList<SomeClass> someClasses)
    {
        //why not List<string> myStrings = new List<string>()
        IList<string> myStrings = new List<string>();

        foreach (var s in someClasses)
        {
            if (s.IsChecked)
            {
                myStrings.Add(s.IsChecked.ToString());
            }
        }
    }

Was bekomme ich jetzt für die Verwendung von IList?

public IList<int> onlySomeInts(IList<int> myInts)
    {
        IList<int> store = new List<int>();
        foreach (var i in myInts)
        {
            if (i % 2 == 0)
            {
                store.Add(i);
            }
        }

        return store;
    }

Wie wäre es jetzt? Gibt es eine neue Implementierung einer Liste von Ints, die ich ändern muss?

Grundsätzlich muss ich einige aktuelle Codebeispiele sehen, wie die Verwendung von IList ein Problem gelöst hätte, wenn nur List in alles aufgenommen worden wäre.

Nach meiner Lektüre hätte ich IEnumberable anstelle von IList verwenden können, da ich mich nur durch die Dinge schlage.

Bearbeiten Also habe ich mit einigen meiner Methoden herumgespielt, wie das geht. Ich bin mir immer noch nicht sicher über den Rückgabetyp (ob ich ihn konkreter oder eine Schnittstelle machen soll).

 public class CardFrmVm
    {
        public IList<TravelFeaturesVm> TravelFeaturesVm { get; set; }
        public IList<WarrantyFeaturesVm> WarrantyFeaturesVm { get; set; }

        public CardFrmVm()
        {
            WarrantyFeaturesVm = new List<WarrantyFeaturesVm>();
            TravelFeaturesVm = new List<TravelFeaturesVm>();
        }
}

 public class WarrantyFeaturesVm : AvailableFeatureVm
    {
    }

 public class TravelFeaturesVm : AvailableFeatureVm
    {
    }

 public class AvailableFeatureVm
    {
        public Guid FeatureId { get; set; }
        public bool HasFeature { get; set; }
        public string Name { get; set; }
    }


        private IList<AvailableFeature> FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm)
        {
            List<AvailableFeature> availableFeatures = new List<AvailableFeature>();
            foreach (var f in avaliableFeaturesVm)
            {
                if (f.HasFeature)
                {
                                                    // nhibernate call to Load<>()
                    AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId);
                    availableFeatures.Add(availableFeature);
                }
            }

            return availableFeatures;
        }

Jetzt gebe ich IList zurück, weil ich dies dann meinem Domain-Modell hinzufügen werde, was eine Eigenschaft wie diese hat:

public virtual IList<AvailableFeature> AvailableFeatures { get; set; }

Das Obige ist eine IList selbst, da dies der Standard zu sein scheint, der mit nhibernate verwendet wird. Andernfalls hätte ich IEnumberable möglicherweise zurückgegeben, bin mir aber nicht sicher. Trotzdem kann ich nicht herausfinden, was der Benutzer zu 100% benötigen würde (hier hat die Rückgabe eines Betons einen Vorteil gegenüber).

Bearbeiten 2

Ich habe auch darüber nachgedacht, was passiert, wenn ich in meiner Methode als Referenz übergeben möchte.

private void FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm, IList<AvailableFeature> toFill)
            {

                foreach (var f in avaliableFeaturesVm)
                {
                    if (f.HasFeature)
                    {
                                                        // nhibernate call to Load<>()
                        AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId);
                        toFill.Add(availableFeature);
                    }
                }
            }

würde ich damit auf Probleme stoßen? Da konnten sie nicht in einem Array (das eine feste Größe hat) übergeben? Wäre es vielleicht besser für eine konkrete Liste?

Antworten:


156

Hier gibt es drei Fragen: Welchen Typ soll ich für einen formalen Parameter verwenden? Was soll ich für eine lokale Variable verwenden? und was soll ich für einen Rückgabetyp verwenden?

Formale Parameter:

Das Prinzip hier ist , nicht mehr zu verlangen, als Sie brauchen . IEnumerable<T>kommuniziert "Ich muss die Elemente dieser Sequenz von Anfang bis Ende bekommen". IList<T>kommuniziert "Ich muss die Elemente dieser Sequenz in beliebiger Reihenfolge abrufen und einstellen". List<T>kommuniziert "Ich muss die Elemente dieser Sequenz in beliebiger Reihenfolge abrufen und einstellen und akzeptiere nur Listen; ich akzeptiere keine Arrays."

Indem Sie mehr verlangen, als Sie benötigen, lassen Sie (1) den Anrufer unnötige Arbeit leisten, um Ihre unnötigen Anforderungen zu erfüllen, und (2) dem Leser Unwahrheiten mitteilen. Fragen Sie nur nach dem, was Sie verwenden möchten. Auf diese Weise muss der Anrufer, wenn er eine Sequenz hat, ToList nicht aufrufen, um Ihre Anforderungen zu erfüllen.

Lokale Variablen:

Verwenden Sie, was Sie wollen. Es ist deine Methode. Sie sind der einzige, der die internen Implementierungsdetails der Methode sehen kann.

Rückgabetyp:

Gleiches Prinzip wie zuvor, umgekehrt. Bieten Sie das Nötigste an, das Ihr Anrufer benötigt. Wenn der Anrufer nur die Möglichkeit benötigt, die Sequenz aufzulisten, geben Sie ihm nur eine IEnumerable<T>.


9
Woher wissen Sie, was der Anrufer benötigt? Zum Beispiel habe ich einen meiner Rückgabetypen auf eine IList <> umgestellt, dann werde ich sie wahrscheinlich sowieso nur aufzählen. Lassen Sie uns einfach eine IEnumberable zurückgeben. Dann schaute ich in meine Ansicht (mvc) und stellte fest, dass ich tatsächlich die Zählmethode benötigte, da ich eine for-Schleife verwenden musste. In meiner eigenen Bewerbung habe ich unterschätzt, was ich tatsächlich brauchte. Wie können Sie vorhersehen, was jemand anderes braucht oder nicht braucht?
Chobo2

@ chobo2: In Ihrem speziellen Beispiel Countfunktioniert LINQ in O (1), wenn Ihr ein IEnumerableist ICollection. Natürlich können Sie auch einfach eine foreachSchleife verwenden.
Brian

6
@ chobo2: Nun, wie rechnen Sie damit, welche Methoden der Anrufer benötigen wird? Das scheint das Problem zu sein, das zuerst gelöst werden muss. Vermutlich haben Sie irgendwie eine Möglichkeit zu wissen, welche Methoden Sie für die Leute schreiben müssen, die sie anrufen werden. Fragen Sie diese Leute, welche Methoden sie zurückgeben möchten. Ihre Frage lautet grundsätzlich: "Woher weiß ich, welche Software ich schreiben soll?" Sie wissen, indem Sie wissen, welche Probleme Ihr Kunde lösen muss, und Code schreiben, der seine Probleme löst.
Eric Lippert

1
@Eric - Sie sollten Ihre Antwort auf formale Parameter aktualisieren, um ein Beispiel für ICollection aufzunehmen, bei dem Sie nur ein Element hinzufügen müssen. Auch Ihre Erklärung zum Rückgabetyp sollte etwas in der Art von "Bieten Sie nur das Nötigste von dem, was Sie dem Anrufer erlauben, auszuführen" sagen. Wenn es eine schreibgeschützte Liste ist, geben Sie nur IEnumerable usw. zurück
Charles Lambert

4
@kvb: Fast. Betrachten Sie Ihr erstes Szenario. Sie können eine algorithmische Verbesserung vornehmen, indem items as IList<T>Sie Folgendes tun. Wenn Sie nicht null erhalten, verwenden Sie Ihren verbesserten Code. Auf diese Weise nutzen Sie den Vorteil, wenn Sie können, und ermöglichen dem Kunden dennoch Flexibilität bei der Weitergabe.
Eric Lippert

33

Der praktischste Grund, den ich je gesehen habe, wurde von Jeffrey Richter in CLR über C # angegeben.

Das Muster besteht darin, die niedrigste Klasse oder Schnittstelle zu verwenden, die für Ihre Argumente möglich ist, und die spezifischste Klasse oder Schnittstelle zurückzugeben, die für Ihre Rückgabetypen möglich ist . Dies gibt Ihren Anrufern die größte Flexibilität bei der Übergabe von Typen an Ihre Methoden und die meisten Möglichkeiten, die Rückgabewerte umzuwandeln / wiederzuverwenden.

Zum Beispiel die folgende Methode

public void PrintTypes(IEnumerable items) 
{ 
    foreach(var item in items) 
        Console.WriteLine(item.GetType().FullName); 
}

Ermöglicht das Aufrufen der Methode als Übergabe in einem beliebigen Typ, der in eine Aufzählung umgewandelt werden kann . Wenn Sie genauer wären

public void PrintTypes(List items)

Wenn Sie dann beispielsweise ein Array hätten und dessen Typnamen auf der Konsole drucken möchten, müssten Sie zuerst eine neue Liste erstellen und diese mit Ihren Typen füllen. Wenn Sie eine generische Implementierung verwenden, können Sie nur eine Methode verwenden, die für jedes Objekt nur mit Objekten eines bestimmten Typs funktioniert .

Wenn Sie über Rückgabetypen sprechen, können Anrufer umso flexibler sein, je spezifischer Sie sind.

public List<string> GetNames()

Mit diesem Rückgabetyp können Sie die Namen iterieren

foreach(var name in GetNames())

oder Sie können direkt in die Sammlung indizieren

Console.WriteLine(GetNames()[0])

Wenn Sie dagegen einen weniger spezifischen Typ zurückbekommen

public IEnumerable GetNames()

Sie müssten den Rückgabetyp massieren, um den ersten Wert zu erhalten

Console.WriteLine(GetNames().OfType<string>().First());

10
Beachten Sie, dass dieser Rat der Antwort von Eric Lippert in der Empfehlung für Rückgabetypen widerspricht. Der Ansatz von Jeffrey Richter bietet den Verbrauchern der Methode die größte Flexibilität, das zurückgegebene Objekt nach Belieben zu verwenden, während der Ansatz von Eric den Betreuern der Methode die größte Flexibilität bietet, die Implementierung zu ändern, ohne die öffentliche Oberfläche der Methode zu ändern. Ich neige dazu, Jeffreys Rat für internen Code zu befolgen, aber für eine öffentliche Bibliothek wäre ich wahrscheinlich eher geneigt, Erics zu folgen.
Phoog

3
@phoog: Wenn man bedenkt, woher Eric kommt, wäre es nicht verwunderlich, dass er vorsichtiger ist, wenn es darum geht, Änderungen zu brechen. Aber es ist definitiv ein gültiger Punkt.

Sehr mutig. Es scheint, als ob es Menschen für beide Seiten gibt (Rückkehr bloß oder nicht). Ich verstehe mehr, warum Sie es für die Parameter tun, die dazu beitragen, unnötige Konvertierungen zu verhindern. Ich bin mir immer noch nicht sicher, was ich mit dem Rückgabetyp machen soll.
Chobo2

@ chobo2: Wirklich, Sie müssen nur überlegen, ob Sie den Rückgabetyp in Zukunft möglicherweise ändern müssen, wenn Sie etwas genaueres als allgemeines angeben. Wenn Sie Ihre Methode in Betracht ziehen können, stellen Sie fest, dass Sie den Rückgabesammlungstyp wahrscheinlich nicht ändern werden. Dann ist es wahrscheinlich sicher, einen genaueren Typ zurückzugeben. Wenn Sie sich nicht sicher sind oder befürchten, dass Sie den Code anderer Personen brechen, wenn Sie ihn in Zukunft ändern, gehen Sie allgemeiner vor.

1
+1 Wissenswertes: Diese Antwort ist ein schönes, CLR-spezifisches Beispiel für das Postelsche Gesetz . :)
Dan J

13

IEnumerable<T>Ermöglicht das Durchlaufen einer Sammlung. ICollection<T>baut darauf auf und ermöglicht auch das Hinzufügen und Entfernen von Elementen. IList<T>ermöglicht auch den Zugriff auf und die Änderung an einem bestimmten Index. Indem Sie diejenige offenlegen, mit der Ihr Verbraucher arbeiten soll, können Sie Ihre Implementierung ändern. List<T>implementiert zufällig alle drei dieser Schnittstellen.

Wenn Sie Ihr Eigentum als List<T>oder sogar IList<T>als verfügbar machen, wenn Ihr Verbraucher nur die Möglichkeit haben soll, die Sammlung zu durchlaufen. Dann könnten sie davon abhängen, dass sie die Liste ändern können. Später, wenn Sie sich entscheiden, den tatsächlichen Datenspeicher von a List<T>in a zu konvertieren Dictionary<T,U>und die Wörterbuchschlüssel als tatsächlichen Wert für die Eigenschaft verfügbar zu machen (genau das musste ich zuvor tun). Dann haben Verbraucher, die erwartet haben, dass sich ihre Änderungen in Ihrer Klasse widerspiegeln, diese Fähigkeit nicht mehr. Das ist ein großes Problem! Wenn Sie das List<T>als IEnumerable<T>anzeigen, können Sie bequem vorhersagen, dass Ihre Sammlung nicht extern geändert wird. Dies ist eine der Möglichkeiten List<T>, eine der oben genannten Schnittstellen verfügbar zu machen.

Diese Abstraktionsebene geht in die andere Richtung, wenn sie zu Methodenparametern gehört. Wenn Sie Ihre Liste an eine akzeptierende Methode übergeben, IEnumerable<T>können Sie sicher sein, dass Ihre Liste nicht geändert wird. Wenn Sie die Person sind, die die Methode implementiert, und Sie sagen, Sie akzeptieren eine, IEnumerable<T>weil Sie nur diese Liste durchlaufen müssen. Dann kann die Person, die die Methode aufruft, sie mit jedem aufzählbaren Datentyp aufrufen. Auf diese Weise kann Ihr Code auf unerwartete, aber absolut gültige Weise verwendet werden.

Daraus folgt, dass Ihre Methodenimplementierung die lokalen Variablen nach Belieben darstellen kann. Die Implementierungsdetails werden nicht angezeigt. Sie können Ihren Code in etwas Besseres ändern, ohne die Personen zu beeinträchtigen, die Ihren Code aufrufen.

Sie können die Zukunft nicht vorhersagen. Angenommen, der Typ einer Eigenschaft ist immer von Vorteil, da dies List<T>Ihre Fähigkeit zur Anpassung an unvorhergesehene Erwartungen an Ihren Code sofort einschränkt. Ja, Sie können diesen Datentyp möglicherweise nie von a ändern, List<T>aber Sie können sicher sein, dass dies erforderlich ist . Ihr Code ist dafür bereit.


9

Kurze Antwort:

Sie übergeben die Schnittstelle so, dass Ihr Code sie unterstützt, unabhängig davon, welche konkrete Implementierung dieser Schnittstelle Sie verwenden.

Wenn Sie eine konkrete Implementierung der Liste verwenden, wird eine andere Implementierung derselben Liste von Ihrem Code nicht unterstützt.

Lesen Sie etwas über Vererbung und Polymorphismus .


8

Hier ein Beispiel: Ich hatte einmal ein Projekt, bei dem unsere Listen sehr groß wurden und die daraus resultierende Fragmentierung des großen Objekthaufens die Leistung beeinträchtigte. Wir haben List durch LinkedList ersetzt. LinkedList enthält kein Array, so dass wir den großen Objekthaufen plötzlich fast nicht mehr verwenden konnten.

Meistens haben wir die Listen IEnumerable<T>sowieso so verwendet, dass keine weiteren Änderungen erforderlich waren. (Und ja, ich würde empfehlen, Referenzen als IEnumerable zu deklarieren, wenn Sie sie nur aufzählen.) An einigen Stellen benötigten wir den Listenindexer, sodass wir einen ineffizienten IList<T>Wrapper um die verknüpften Listen geschrieben haben. Wir brauchten den Listenindexer selten, daher war die Ineffizienz kein Problem. Wenn dies der Fall gewesen wäre, hätten wir eine andere Implementierung von IList bereitstellen können, möglicherweise als Sammlung von ausreichend kleinen Arrays, die effizienter indizierbar gewesen wäre und gleichzeitig große Objekte vermieden hätte.

Am Ende müssen Sie möglicherweise eine Implementierung aus irgendeinem Grund ersetzen. Leistung ist nur eine Möglichkeit. Unabhängig vom Grund wird durch die Verwendung des am wenigsten abgeleiteten Typs die Notwendigkeit von Änderungen in Ihrem Code verringert, wenn Sie den spezifischen Laufzeittyp Ihrer Objekte ändern.


3

Innerhalb der Methode sollten Sie varanstelle von IListoder verwenden List. Wenn sich Ihre Datenquelle ändert und stattdessen von einer Methode stammt, onlySomeIntsüberlebt Ihre Methode.

Der Grund für die Verwendung IListanstelle von Listals Parameter ist, dass viele Dinge implementiert werden IList(List und [] als zwei Beispiele), aber nur eines implementiert wird List. Es ist flexibler, auf der Schnittstelle zu codieren.

Wenn Sie nur über die Werte aufzählen, sollten Sie verwenden IEnumerable. Jeder Datentyp, der mehr als einen Wert enthalten kann, implementiert IEnumerable(oder sollte) und macht Ihre Methode äußerst flexibel.


13
Zu sagen, er sollte "var" verwenden, ist völlig falsch. Es ist egal, was er dort verwendet - dies ist ein Stilproblem. Dies hat keinen Einfluss auf die Signatur der Methode und ist beim Kompilieren in Stein gemeißelt. Sie sollten ihm stattdessen helfen, seine Verwirrung darüber zu überwinden, dass er seine lokale Liste als IList foo = new List deklariert - hier liegt seine Verwirrung eindeutig.
x0n

Sie sagen also im Grunde, dass Sie IList nur deshalb aufnehmen sollen, wenn sie ein Array senden möchten, das sie nicht zuerst tun müssen. Wie wäre es mit einer Rücksendung? Ich verstehe nicht, was Sie unter "Methode wird überleben" verstehen, wenn Sie Vars verwenden? Ich weiß, es wird ein bisschen mehr Arbeit sein, aber müssen Sie das nicht auch auf den neuen Typ umstellen? Also vielleicht IList <int> zu IList <String>?
Chobo2

Der Punkt "use var" war zwar eher ein Vorschlag, sich innerhalb der Methode selbst keine Sorgen zu machen und sich mehr darauf zu konzentrieren, wie es für die Verbraucher aussieht.
Bryan Boettcher

Ich stimme @ x0n zu: var wird stark überstrapaziert und hilft nicht, etwas klarer zu machen.
Dieser Chuck Guy

1

Die Verwendung von IList anstelle von List erleichtert das Schreiben von Komponententests erheblich. Sie können eine 'Mocking'-Bibliothek verwenden, um Daten zu übergeben und zurückzugeben.

Der andere allgemeine Grund für die Verwendung von Schnittstellen besteht darin, dem Benutzer eines Objekts das erforderliche Mindestmaß an Wissen zur Verfügung zu stellen.

Betrachten Sie den (erfundenen) Fall, in dem ich ein Datenobjekt habe, das IList implementiert.

public class MyDataObject : IList<int>
{
    public void Method1()
    {
       ...
    }
    // etc
}

Ihre obigen Funktionen kümmern sich nur darum, eine Liste durchlaufen zu können. Im Idealfall sollten sie nicht wissen müssen, wer diese Liste implementiert oder wie sie sie implementiert.

In Ihrem Beispiel ist IEnumerable die bessere Wahl, als Sie dachten.


1

Es ist immer eine gute Idee, die Abhängigkeiten zwischen Ihrem Code so weit wie möglich zu reduzieren.

Vor diesem Hintergrund ist es am sinnvollsten, Typen mit der geringstmöglichen Anzahl externer Abhängigkeiten zu übergeben und diese zurückzugeben. Dies kann jedoch je nach Sichtbarkeit Ihrer Methoden und deren Signaturen unterschiedlich sein.

Wenn Ihre Methoden Teil einer Schnittstelle sind, müssen die Methoden mithilfe der für diese Schnittstelle verfügbaren Typen definiert werden. Betontypen werden wahrscheinlich nicht für Schnittstellen verfügbar sein, sodass sie nicht konkrete Typen zurückgeben müssten. Sie möchten dies tun, wenn Sie beispielsweise ein Framework erstellen.

Wenn Sie jedoch kein Framework schreiben, kann es vorteilhaft sein, Parameter mit den schwächsten möglichen Typen (dh Basisklassen, Schnittstellen oder sogar Delegaten) zu übergeben und konkrete Typen zurückzugeben. Dies gibt dem Aufrufer die Möglichkeit, so viel wie möglich mit dem zurückgegebenen Objekt zu tun, selbst wenn es als Schnittstelle umgewandelt wird. Dies macht die Methode jedoch anfälliger, da jede Änderung des zurückgegebenen Objekttyps den aufrufenden Code beschädigen kann. In der Praxis ist dies jedoch im Allgemeinen kein großes Problem.


1

Sie akzeptieren eine Schnittstelle als Parameter für eine Methode, da der Aufrufer dadurch verschiedene konkrete Typen als Argumente übergeben kann. In Anbetracht Ihrer Beispielmethode LogAllChecked kann der Parameter someClasses von verschiedenen Typen sein, und für die Person , die die Methode schreibt , können alle gleichwertig sein (dh Sie würden unabhängig vom Typ des Parameters genau denselben Code schreiben). Aber für die Person, die die Methode aufruft , kann dies einen großen Unterschied machen - wenn sie ein Array hat und Sie nach einer Liste fragen, müssen sie das Array bei jedem Aufruf der Methode in eine Liste oder vv ändern, was eine totale Verschwendung von ist Zeit von einem Programmierer und Performance POV.

Ob Sie eine Schnittstelle oder einen konkreten Typ zurückgeben, hängt davon ab, was Ihre Anrufer mit dem von Ihnen erstellten Objekt tun möchten. Dies ist eine API-Entwurfsentscheidung, und es gibt keine feste Regel. Sie müssen ihre Fähigkeit, das Objekt vollständig zu nutzen, gegen ihre Fähigkeit abwägen, einen Teil der Objektfunktionalität problemlos zu nutzen (und natürlich, ob Sie möchten, dass sie das Objekt vollständig nutzen). Wenn Sie beispielsweise eine IEnumerable zurückgeben, beschränken Sie diese auf die Iteration. Sie können Ihrem Objekt keine Elemente hinzufügen oder daraus entfernen, sondern nur gegen die Objekte vorgehen. Wenn Sie eine Sammlung außerhalb einer Klasse verfügbar machen müssen, aber nicht möchten, dass der Aufrufer die Sammlung ändert, ist dies eine Möglichkeit. Wenn Sie andererseits eine leere Sammlung zurückgeben, von der Sie erwarten, dass sie gefüllt wird,


0

Hier ist meine Antwort in dieser .NET 4.5+ Welt.

Verwenden IList <T> und IReadonlyList <T> ,
              statt List <T> , weil ReadonlyList <T> ist nicht vorhanden.

IList <T> sieht so konsistent mit IReadonlyList <T> aus

  • Verwenden Sie IEnumerable <T> für minimale Exposition (Eigenschaft) oder Anforderung (Parameter), wenn nur foreach verwendet werden kann.
  • Verwenden Sie IReadonlyList <T>, wenn Sie auch Count und [] Indexer verfügbar machen / verwenden müssen .
  • Verwenden Sie IList <T>, wenn Sie Anrufern auch das Hinzufügen / Aktualisieren / Löschen von Elementen erlauben

Da List <T> IReadonlyList <T> implementiert , ist kein explizites Casting erforderlich.

Eine Beispielklasse:

// manipulate the list within the class
private List<int> _numbers;

// callers can add/update/remove elements, but cannot reassign a new list to this property
public IList<int> Numbers { get { return _numbers; } }

// callers can use: .Count and .ReadonlyNumbers[idx], but cannot add/update/remove elements
public IReadOnlyList<int> ReadonlyNumbers { get { return _numbers; } }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.