DCSIMG
Encapsulation & Exposure - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

Encapsulation & Exposure

I like to keep a tight leash on my classes. No unnecessary exposure. Consider the following simple class:

class PersonnelManager {
    List<Person> _people = new List<Person>();

    public void Add(Person person) {
        // do some validation
        _people.Add(person);
        // maybe some extras
    }
}

I want to expose the currently managed Person objects for interested parties. One way to do this would be:

public List<Person> People {
    get { return _people; }
}

This may be obvious – but I’ve seen this too many times. Exposing the property as List<>, IList<> or ICollection<> defeats the purpose. Anyone can call Add on the returned object.

A better alternative is to use IEnumerable<>:

public IEnumerable<Person> People {
    get { return _people; }
}

This is much better: no Add method visible. Still, curious (and malicious) clients may try this:

var mgr = new PersonnelManager();
var list = mgr.People as List<Person>;
if(list != null)
    list.Add(new Person { Age = 10, Name = "Bart" });

Or better (lower common denominator):

var list = mgr.People as ICollection<Person>;
if(list != null)
    list.Add(new Person { Age = 10, Name = "Bart" });

This will work with other lists, such as ObservableCollection<>.

Despite our best efforts, Person objects can still be added “behind our backs”. Although it’s less likely than the first alternatives, I still would have liked something “stronger”.

The best I could come up with is using yield:

public IEnumerable<Person> People {
    get {
        foreach(var p in _people)
            yield return p;
    }
}

This has the desired effect: No cast to ICollection<> or IList<> would succeed, as the compiler re-writes the code and creates its own implementation of IEnumerable<Person> (which does not include other interfaces). The downside, of course, is the added code we had to write.

Is this really bulletproof? Almost. A full trust application can still theoretically use reflection to get to the private List<Person> member and call Add on it directly, but it’s a lot less likely, as such code is, naturally, cumbersome and fragile.

Comments List

# re: Encapsulation & Exposure

Published at Tuesday, September 27, 2011 7:33 AM by DavidBi  

Why not used the List(Of T).AsReadOnly Method?, according to MSDN: "To prevent any modifications to List(Of T), expose List(Of T) only through this wrapper.

A collection that is read-only is simply a collection with a wrapper that prevents modifying the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes.

This method is an O(1) operation."

# re: Encapsulation & Exposure

Published at Tuesday, September 27, 2011 8:05 AM by pavely  

Certainly possible. I just don't like the Add method to exist - not just throw an exception.

Leave a Comment

(required) 
(
required
)
 
(optional)
(required) 

Enter the numbers above: