.NET Collection Management

This article focuses on managing a collection of objects using generics, an important addition to the .NET Framework 2.0, and examines the capabilities provided by the generic list.

Using generics to manage a collection of objects

Introduction

Generics refer to classes and methods that work uniformly on values of different types . They offer some rich capabilities that enhance the coding experience through better performance and easier maintenance. Generics enable you to define classes that use a generic type, and define the type at the time of instantiation or method calls. This makes your code strongly typed, but makes maintenance easier.

Generics enable you to define classes, interfaces, delegates or methods with placeholders for parameterized types used within. At the time of instantiation or method calls, these placeholders are replaced with concrete types.

Let’s say that you need to manipulate a collection of strings. You can use a collection object that is not strongly typed – that is, it manipulates a collection of object instances – or you can create a strongly typed collection to handle a collection of strings.

The first approach will be prone to error because your code will not be restrictive enough nor strongly typed. In the second approach, defining a class that derives from collection for each strongly typed collection you require becomes a maintenance nightmare, since you need to manage many collection classes.

Using generics, a single collection is defined and the type of the objects contained inside is marked with a placeholder. When creating a collection, you specify the type the placeholder denotes.

Generic collections

The .NET Framework 2.0 provides generic collections and interfaces that make it easier to manipulate a collection or define your own. These classes are contained within the namespace System.Collections.Generics.

Below is a simple example that uses a generic List<T> class. Let’s see how we can use it to manipulate a collection of objects, but let’s first define a simple Employee class that we will use inside our list:

C# Code:

Defining and using a collection of employees is simplified when using the List<T>. The <T> denotes the placeholder for the type the list contains. We can define our strongly typed collection by replacing the placeholder with the actual type – “Employee” in our example.

C# Code

Sorting a list

Sorting collections is also simplified using the generic list. The System.Collections.Generic.List has a sort method that accepts a generic Comparison<T>. Comparison<T> is a delegate that takes two parameters of type T, where T is the type (Employee) used to instantiate our collection.

Since we are handling a collection of employees in our sample code, Comparison<Employee> will be a delegate that points to a method that accepts two employee objects. An integer result is returned after the comparison is performed between the two employee objects passed to it.

C# Code

The method above compares two employees by salary and returns the comparison result. To sort our generic employee list, we simple plug the sort method into the Comparison<Employee> delegate.

C# Code

Searching a list

Searching collections is a matter of using a delegate that does the filtering for us. In our collection example, the Find and FindAll methods in List<T> use a Predicate<T> delegate that accepts a parameter of type T. T is the type used to instantiate our collection that returns a boolean indicating whether or not a match is found.

The following class wraps a method that accepts an employee and indicates in the return whether the salary of the employee is greater than the amount.

C# Code

The above class encapsulates the function of searching by the salary of an employee.

To search our previous list and return all the employees who have a salary greater than 1,000, we need to point to our method defined inside SalaryFilter using a Predicate<Employee> delegate, and pass it to the Find method.

C# Code

The difference between the Find and FindAll methods is that Find returns a single object and FindAll returns a collection of objects using a list of the type we are handling.

Iterating a list

Iterating objects and performing simple operations on them in the list has been simplified in .NET 2.0. In .NET 1.1, you would use a ForEach loop to loop through a collection of objects and perform actions. In .NET 2.0, you can use the Action<T> delegate to perform operations on objects inside a list.

Let’s create a class that encapsultes functionality to add the salaries of the employee objects.

C# Code

We point to the CalculateSalary method using an Action<Employee> delegate, and pass it to the ForEach method of the list.

C# Code

List conversion

Converting a list is useful when you want a subset of the data in the collection. If you have a collection of employee objects, for example, you might need to retrieve a collection of string objects of the employee names.

In .NET 1.1, you would have to loop through the employee objects, add one to a new collection and return it. In .NET 2.0, you can simply point to a method using a Converter<TInput,TOutput> delegate that accepts a parameter of type TInput (in our case Employee) and it returns an object of type TOutput.

C# Code

The method above shows how an employee object should be converted to a string object. To convert the entire list, we use the method above with a Converter<Employee,string> delegate.

C# Code

Conclusion

This article reviewed the enhancements in .NET 2.0 that enable generic List<T> to better handle collections. Some special features implemented using generics were also introduced.