Frage

Gibt es einen Grund eine interne Sammlung als Readonlycollection eher als ein IEnumerable aussetzen, wenn der Angerufene Code nur iteriert über die Sammlung?

class Bar
{
    private ICollection<Foo> foos;

    // Which one is to be preferred?
    public IEnumerable<Foo> Foos { ... }
    public ReadOnlyCollection<Foo> Foos { ... }
}


// Calling code:

foreach (var f in bar.Foos)
    DoSomething(f);

Wie ich es sehe IEnumerable eine Teilmenge der Schnittstelle von Readonlycollection ist, und es ist nicht der Benutzer die Sammlung zu ändern. Wenn also die IEnumberable Schnittstelle genug ist, dann ist, dass die zu verwenden. Ist das eine richtige Art und Weise über sie der Argumentation oder bin ich etwas fehlt?

Danke / Erik

War es hilfreich?

Lösung

Modernere Lösung

Wenn Sie nicht die interne Sammlung müssen wandelbar sein, könnten Sie die System.Collections.Immutable Paket, ändern Sie Ihren Feldtyp eine unveränderliche Sammlung zu sein, und setzt dann diese direkt -. unter der Annahme Foo selbst ist unveränderlich, natürlich

aktualisiert Antwort auf die Frage mehr direkt

Adresse
  

Gibt es einen Grund eine interne Sammlung als Readonlycollection eher als ein IEnumerable aussetzen, wenn der Angerufene Code nur iteriert über die Sammlung?

Es hängt davon ab, wie viel Sie die Telefonvorwahl vertrauen. Wenn Sie in der vollständigen Kontrolle über alles sind, die jemals dieses Mitglied anrufen und Sie Garantie , dass kein Code jemals verwenden werden:

ICollection<Foo> evil = (ICollection<Foo>) bar.Foos;
evil.Add(...);

dann sicher, wird kein Schaden getan werden, wenn Sie nur die Sammlung direkt zurück. Ich versuche gewöhnlich etwas paranoid als dass, obwohl zu sein.

Ebenso wie Sie sagen: wenn Sie Notwendigkeit IEnumerable<T>, warum sich binden zu etwas stärker

Original Antwort

Wenn Sie .NET 3.5, verwenden, können Sie verhindern, dass eine Kopie und vermeiden, dass die einfache Besetzung durch einen einfachen Anruf mit überspringen:

public IEnumerable<Foo> Foos {
    get { return foos.Skip(0); }
}

(Es gibt viele andere Möglichkeiten für triviales Einwickeln - der schöne an Skip über Select / Wo ist, dass es keine Delegierten für jede Iteration auszuführen zwecklos.)

Wenn Sie nicht .NET 3.5 verwenden Sie einen sehr einfachen Wrapper schreiben können, das Gleiche zu tun:

public static IEnumerable<T> Wrapper<T>(IEnumerable<T> source)
{
    foreach (T element in source)
    {
        yield return element;
    }
}

Andere Tipps

Wenn Sie nur durch die Sammlung iterieren müssen:

foreach (Foo f in bar.Foos)

dann Rückkehr IEnumerable ist genug.

Wenn Sie zufällig Zugriff auf Elemente:

Foo f = bar.Foos[17];

dann wickeln Sie es in Readonlycollection .

Wenn Sie dies tun, dann gibt es nichts, Ihre Anrufer Stoppen der IEnumerable Gießen zu ICollection zurück und ändern Sie es dann. Readonlycollection entfernt diese Möglichkeit, obwohl es immer noch möglich ist, die zugrunde liegende beschreibbare Sammlung über Reflexion zuzugreifen. Wenn die Sammlung klein ist dann eine sichere und einfache Möglichkeit, um dieses Problem zu bekommen, ist stattdessen eine Kopie zurück.

Ich vermeide Readonlycollection so viel wie möglich zu verwenden, es ist tatsächlich wesentlich langsamer als nur eine normale Liste. Sehen Sie dieses Beispiel:

List<int> intList = new List<int>();
        //Use a ReadOnlyCollection around the List
        System.Collections.ObjectModel.ReadOnlyCollection<int> mValue = new System.Collections.ObjectModel.ReadOnlyCollection<int>(intList);

        for (int i = 0; i < 100000000; i++)
        {
            intList.Add(i);
        }
        long result = 0;

        //Use normal foreach on the ReadOnlyCollection
        TimeSpan lStart = new TimeSpan(System.DateTime.Now.Ticks);
        foreach (int i in mValue)
            result += i;
        TimeSpan lEnd = new TimeSpan(System.DateTime.Now.Ticks);
        MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
        MessageBox.Show("Result: " + result.ToString());

        //use <list>.ForEach
        lStart = new TimeSpan(System.DateTime.Now.Ticks);
        result = 0;
        intList.ForEach(delegate(int i) { result += i; });
        lEnd = new TimeSpan(System.DateTime.Now.Ticks);
        MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
        MessageBox.Show("Result: " + result.ToString());

Manchmal können Sie eine Schnittstelle verwenden, vielleicht, weil Sie die Sammlung während Unit-Tests verspotten wollen. Bitte beachten Sie meine Blog-Eintrag rel="nofollow für die eigene Schnittstelle zu Readonlycollection Zugabe unter Verwendung von ein Adapter.

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