Udostępnianie kolekcji tylko do odczytu

2013-01-15

Poniżej znajduje się definicja klasy zawierającej kolekcję adresów e-mail danej osoby. Dostęp do listy adresów uzyskujemy poprzez właściwość Emails. Za dodawanie nowych adresów odpowiada z kolei metoda AddEmail. Przed dodaniem elementu do kolekcji sprawdzana jest jego poprawność oraz to czy taki adres nie znajduje się już na liście.

class Person
{
    private List<string> emails;

    public IList<string> Emails
    {
        get { return emails; }
    }

    public Person()
    {
        emails = new List<string>();
    }

    public void AddEmail(string email)
    {
        if (email != null && email.Contains('@') && !emails.Contains(email))
            emails.Add(email);
        else
            throw new ArgumentException();
    }
}

Teoretycznie powyższa definicja klasy jest poprawna. Co się jednak stanie po wykonaniu następującego kodu?:

Person person = new Person();

person.AddEmail("email@mail.com");
person.Emails.Add("email@mail.com");
person.Emails.Add(null);

Okazuje się, że możliwe jest ominięcie walidacji zastosowanej w metodzie AddEmail poprzez dodawanie elementów bezpośrednio do kolekcji. Co więcej, w ten sam sposób można je również usuwać.
Rozwiązaniem tych problemów jest udostępnienie kolekcji tylko do odczytu. Jest to możliwe dzięki wykorzystaniu obiektu ReadOnlyCollection<T> (przestrzeń System.Collections.ObjectModel). Klasa List<T> posiada metodę AsReadOnly() zwracającą obiekt tego typu. Oto jej zastosowanie we właściwości naszej klasy:

class Person
{
    private List<string> emails;

    public IList<string> Emails
    {
        get { return emails.AsReadOnly(); }
    }

    public Person()
    {
        emails = new List<string>();
    }

    public void AddEmail(string email)
    {
        if (email != null && email.Contains('@') && !emails.Contains(email))
            emails.Add(email);
        else
            throw new ArgumentException();
    }
}

W tej chwili przy próbie wykonania jakiejkolwiek operacji dodawania, usuwania lub zastąpienia elementu kolekcji zgłoszony zostanie wyjątek:

ReadOnly1

Obiekt ReadOnlyCollection<T> może zostać utworzony dla dowolnej kolekcji implementującej IList<T>:

ObservableCollection<string> collection = new ObservableCollection<string>();
//...
ReadOnlyCollection<string> roCollection = new ReadOnlyCollection<string>(collection);

ReadOnlyCollection jest wraperem na kolekcję zabezpieczającym ją przed modyfikacjami. Nie tworzy on ponownie elementów a jedynie przechowuje oryginalną kolekcję, dzięki czemu wszelkie wykonywane bezpośrednio na niej operacje są w nim odzwierciedlane. Zasada działania ReadOnlyCollection jest bardzo prosta: jeżeli wywołana zostanie na nim metoda modyfikująca kolekcję pojawi się wyjątek, w przeciwnym wypadku ta sama metoda zostanie wywołana na oryginalnej kolekcji.

Więcej informacji można znaleźć na MSDN: ReadOnlyCollection, List<T>.AsReadOnly.

Reklamy

Posted on 2013-01-15, in .NET/C# and tagged , , . Bookmark the permalink. 3 Komentarze.

  1. W .NET 4.5 za pomocą dodatkowych bibliotek pobranych NuGet można skorzystać z czegos co się nazywa Immutable Collections, które tak de facto w 100% zabezpieczają przed niezmiennoscia kolekcji w prosty sposób 🙂
    http://blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx

  2. Dzięki za post!
    Wygląda przydatnie ale czy nie gryzie się to jakość np. z NHibiernate?

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s

%d blogerów lubi to: