Don’t Repeat Yourself using LINQ & Delegates

In one of my projects, I had to look for certain objects using certain qualifiers: ID, Name, ID & Name, etc… Without optional parameters (Hello C# 4.0!), I ended up with copying & pasting whole blocks of code: initializing, filtering, returning the result, error handling, etc. I wondered if there was an easier solution to that and then it hit me! LINQ & delegates!

Let’s start with a simple class:

1
2
3
4
5
6
7
public class Employee
{
    public string FirstName { get; protected set; }
    public string LastName { get; protected set; }
    public int Id { get; protected set; }
    //...and other methods...
}

Then, we have a List of Employees:

1
2
3
4
5
6
List<Employee> Employees = new List<Employee>()
    {
        new Employee("Juan", "dela Cruz", 1),
        new Employee("John", "Doe", 2),
        //and so on...
    };

Now let’s try making some convenience methods for retrieving a collection of filtered Employees.

1
2
3
4
5
6
7
8
9
10
11
12
13
public IEnumerable<Employee> GetByLastName(string last)
{
    IEnumerable<Employee> result =
        Employees.Where(emp => emp.LastName == last);
    return result;
}
 
public IEnumerable<Employee> GetById(int id)
{
    IEnumerable<Employee> result =
        Employees.Where(emp => emp.Id == id);
    return result;
}

Quick & easy, yes? Now what if instead of a List, our collection of Employees isn’t that stable. We’d need to put each of them in try-catch blocks and add error handling. This can quickly become high-maintenance code.

Enter LINQ and delegates!

We then basically need one method that will contain all the error handling, initialization and whatnot. We just pass a delegate will filter the collection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public IEnumerable<Employee> GetEmployee(
    Func<Employee, bool> query)
{
    try
    {
        //initialization
        IEnumerable<Employee> result =
                Employees.Where(query);
        return result;
    }
        //handling other exceptions...
    catch (Exception ex)
    {
        //Error handling
        return null;
    }
}

Did you notice: Func<Employee, bool>? All we need is to pass an expression that uses the Employee as input and returns a boolean (true if it passes the condition or false if not).

What does this mean? Well, we can now replace the convenience methods we wrote earlier with this:

1
2
3
4
5
6
7
8
9
10
11
12
public IEnumerable<Employee> GetByFirstName(string first)
{
    Func<Employee, bool> query =
        emp => emp.FirstName == first;
    return GetEmployee(query);
}
 
public IEnumerable<Employee> GetById(int id)
{
    Func<Employee, bool> query = emp => emp.Id == id;
    return GetEmployee(query);
}

We’re back to code that’s easy to maintain with all the initializations, error handling and other code blocks that you need.

You could even do away with the convenience methods and use lambda expressions all the way!

1
2
3
4
GetEmployee(emp => true); //get everything
GetEmployee(emp => emp.LastName.Contains("Locs"));
GetEmployee(emp => emp.FirstName == "Richard"
    &#038;&#038; emp.LastName == "Locsin");

Have fun!


Related resources:

Comments May 29th, 2009

blog comments powered by Disqus