multi-cast delegates and Predicate<T>

Since Predicate<T> that is used for finding stuff in a generics list is in fact a multi-cast delegate, I thought it might be possible to chain several Predicates to simplify multiple requirements for the elements returned.

Consider the following code:

Predicate<int> greaterThan5 = delegate(int x) { return x > 5; };
Predicate<int> lessThan10 = delegate(int x) { return x < 10; };

It would be cool if it would be possible to chain them, by doing a simple:

Predicate<int> combined = lessThan10 + greaterThan5;

And then:

List<int> resulting = allInts.FindAll(combined);

Unfortunately it dosen't work, or more precicely, it is only the last delegate that gets invoked.

It seems like MS did not envision people wanting to use multi-cast delegates for their predicates, and when looking at the List.FindAll implementation, its very clear, since it only invokes the delegate as it were a single cast one:

public List<T> FindAll(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    List<T> list = new List<T>();
    for (int i = 0; i < this._size; i++)
    {
        if (match(this._items[i]))
        {
            list.Add(this._items[i]);
        }
    }
    return list;
}


So unfortunately you still need to do the old fashion way:

List<int> resulting = allInts.FindAll(lessThan10).FindAll(greaterThan5);

Lets hope they change it in c# 3.0

Delegates

Some years ago, when someone mentioned delegates I would shudder, and think back of all the times when I had tried to use them to something useful, and All I could think of at the time was to use them for events that other objects could subscribe to. This is also a nice feature, but when you think about it delegates is so much more.

I have started using delegates more heavily, and primarily to do Lazy Loading of data from a database. Lazy Loading is a technique where you only load the data when the application need it.  Lets say you have a class Customer, with a lot of properties, including an Orders collection. Instead of loading all orders and all other data that is related to the Customer, why not just load the data when the application makes a request for it, by invoking the get method of the Orders collection.

People would perhaps argue that it would require for the business objects to know the database structure, or if done a little more clever, need a reference to a Data Access Layer, but no, thats not neccesary, since you can use delegates for it.

consider the following delegate:

public delegate List<T> LoadDataDelegate<T>();


This delegate states that it will return a list of objects of type T.

okay then consider the following class

public class Customer
{

    /// <summary>
    /// Creates a new instance of the Customer class
    /// </summary>
    /// <param name="ordersDelegate"></param>
    public Customer(LoadDataDelegate<Order> ordersDelegate)
    {
        this.ordersDelegate = ordersDelegate;
    }

    private LoadDataDelegate<Order> ordersDelegate;

    private List<Order> orders;
    /// <summary>
    /// Gets a List of orders for the current Customer
    /// </summary>
    public List<Order> Orders
    {
        get
        {
            if (orders == null)
            {
                orders = ordersDelegate();
            }
            return orders;
            }
    }
   
    
}


And the following Mock data layer

public class DAL
{
    public List<Order> GetAllOrdersForCustomer(long customerID)
    {
        string sql = "select * from orders where customer_id=@customer_id";
        //etc
        //load into list
        }

        public Customer GetCustomer(long customerID)
        {
            Customer c = new Customer(delegate() { return GetAllOrdersForCustomer(customerID); });
            //load other data from the database

            return c;
        }
}


These two classes and the delegates, show a very simple way of using delegates for something intelligent and create an object that is capable of loading data from the database on demand, without knowing anything about Datalayers, SQL or anything else.

Nice right :)

Consider implementing all methods that can create, read, update and delete an object as delegates and all you would ever need to expose to your application is the business objects, and perhaps a few factory methods that could create new instances of objects with the delegates assigned to actual methods in your data layers :)