Building Custom Collections

Chapter 15 covered the standard query operators—that is, the extension methods on IEnumerable<T> that provide methods common to all collections. However, these operators do not make all collections equally suited for all tasks; there is still a need for different collection types. Some collections are better suited to searching by key, whereas others are better suited to accessing items by position. Some collections act like queues: The first element in is the first out. Others are more like stacks: The first element in is the last out. Others are not ordered at all.

The .NET frameworks provide a plethora of collection types suited to many of the scenarios in which collections are needed. This chapter introduces some of these collection types and the interfaces they implement. It also describes how to create custom-built collections that support standard functionality, such as indexing. In addition, it explores the use of the yield return statement to create classes and methods that implement IEnumerable<T>. This feature1 greatly simplifies implementation of collections that can be enumerated with the foreach statement.

Many nongeneric collection classes and interfaces are available in the Microsoft .NET Framework, but in general, these exist today only for backward compatibility with code written before generics came into use. The generic collection types are both faster, because they avoid boxing costs, and more type-safe than the nongeneric collections. Thus, new code should almost always use the generic collection types exclusively. Throughout this book, we assume that you are primarily using generic collection types.

More Collection Interfaces

We’ve already seen how collections implement IEnumerable<T>, the primary interface that enables iteration over the elements of a collection. Many additional interfaces exist that are implemented by more complex collections. Figure 17.1 shows the hierarchy of interfaces implemented by collection classes.