Skip navigation.

Scenario-Driven Design Violated To the UtmostAll recent postsJoin the Dark Side

Collection Interfaces Smell

Going back to Framework Design Guidelines, here’s a guideline pertaining to collections that I disagree with:

8.3.1. Collection Parameters

“✓ DO use the least specialized type possible as a parameter type. Most members taking collections as parameters use the IEnumerable<T> interface.”

public void PrintNames (IEnumerable<string> names) {
   foreach (string name in names)
      Console.Write (name);
}

My grievance with this advice is

  1. it leads to code maintenance nightmare;
  2. it has bad smell of a leaked implementation detail;
  3. it can lead to poor performance of LINQ queries.

Maintenance

I’m a big believer in programming against interfaces instead of concrete implementations. However, when it comes to collections, developers tend to get confused which “least derived” interface (or collection) should do the job. You see IList<T>, List<T>, ICollection<T>, Collection<T>, IEnumerable<T> all used without consistency. Mainintaining this is next to a nightmare.

Implementation leakage

Honestly, I don’t want to care what kind of collection a return type is. Dealing with a specific interface puts me in the wrong frame of mind when consuming the API.

Performance of LINQ queries

Let’s look at a simple operator, such as Last (see ripped source). If the source parameter is an IList<T>, the operator simply returns its Count property. If source implements the vanilla IEnumerator<T>, the code iterates over the entire collection to arrive at the last item. Not too performant, and it’s a simple case!

Using subclasses of generic collections instead

Luckily, the same section in Framework Design Guidelines has the following guideline:

“✓CONSIDER using subclasses of generic base collections instead of using the collections directly. This allows for a better name and for adding helper methods that are not present on the base collection types. This is especially applicable to high-level APIs.”

Collection inheritance is a whole different subject, so I won’t go there. I like the idea of abstracting away from the underlying type, even if it’s as simple as:

public CustomerCollection : Collection<Customer> {}

LINQ-ifying custom collections

While we’re on the subject of custom collections, here’s my approach to making them “LINQ-ready” (same code I demoed at my Philly Code camp LINQ talk):

class CustomerCollection : Collection<Customer>
{
    public CustomerCollection () { }
    public CustomerCollection (IList<Customer> customers) : 
           base (customers) { }

    public static implicit operator
          CustomerCollection (List<Customer> customers)
    {
        return new CustomerCollection (customers);
    }
}

They key here is the implicit conversion operator which takes a list of customers and feeds it to a corresponding constructor. Here’s how to use it in real life:

CustomerCollection result = customers
   .Where ([lambda expression])
   .ToList ();

Nice and concise. Otherwise you’d have to wrap the result with an explicit constructor invocation which gets ugly fast.

Comments

Comment permalink 1 Richard |
Rather than adding an implicit conversion from List to CustomerCollection, why not add a "ToCustomerCollection" extension method? Implicit conversions have a tendency to break things in interesting ways.


public static CustomerCollection ToCustomerCollection(this IEnumerable{Customer} customers)
{
IList{Customer} list = customers as IList{Customer};
if (null == list) list = new List{Customer}(customers);
return new CustomerCollection(list);
}


NB: As all HTML tags are stripped, I've had to use X{T} to represent generic types.

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):