Nov 19, 2007

Anonymous Delegates, how do I love thee? Let me count the ways...

This isn't anything new but I came across a situation recently where I was able to re-factor a seemingly trivial piece code to use an anonymous delegate and it reminded me how cool they are.

One great thing about generics is you can create a collection of strongly typed custom objects and iterate that collection to perform any number of operations. Often times you want to locate an item in the collection given a certain criteria.

I put together a simple little example to show the situation pre-generics,post generics without anonymous delegates, and post generics with anonymous delegates.

To set the scene let's assume we have a person class defined as follows

public class Person   

{
private int id;

private string firstName = string.Empty;
private string lastName = string.Empty;


public int ID
{
get { return id; }
set { id = value; }
}

public string FirstName
{
get { return firstName; }
set { firstName = value; }
}

public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}


Prior to generics you might load an ArrayList of person objects like so:



static ArrayList LoadPersonList()
{
ArrayList list = new ArrayList();

Person Bob = new Person();
Bob.ID = 1;
Bob.FirstName = "Bob";
Bob.LastName = "Smith";

Person Sally = new Person();
Sally.ID = 2;
Sally.FirstName = "Sally";
Sally.LastName = "Jones";

list.Add(Bob);
list.Add(Sally);

return list;
}


So working from this example if we wanted to find the Person record with the first name Bob we could do something like this.



First write a find method that searches the ArrayList for the given first name. Something like this :



static Person FindPersonByFirstName(string firstName, ArrayList personList)
{
foreach (Person p in personList)
{
if (p.FirstName == firstName)
{
return p;
}
}
return null;
}


Then we call the find method and pass the FirstName "Bob":



static void Main(string[] args)
{
ArrayList personList = LoadPersonList();
Person bob = FindPersonByFirstName("Bob", personList);
}


Not so bad. With generics we get a little better.



First our load method now looks like this:



static List<Person> LoadPersonList()
{
List<Person> list = new List<Person>();

Person Bob = new Person();
Bob.ID = 1;
Bob.FirstName = "Bob";
Bob.LastName = "Smith";

Person Sally = new Person();
Sally.ID = 2;
Sally.FirstName = "Sally";
Sally.LastName = "Jones";

list.Add(Bob);
list.Add(Sally);

return list;
}


And our find method has been simplified to look like this



 



private static bool FindPersonByFirstName(Person p)
{
if (p.FirstName == "Bob")
return true;
else
return false;
}


And now we use the List<T> Find method like this to locate the item in the list that we want:



static void Main(string[] args)
{
List<Person> personList = LoadPersonList();
Person found = personList.Find(FindPersonByFirstName);
}


You notice that we are now hard-coding "Bob" in the implementation of the Find method. That obviously isn't ideal. We need a better solution for passing arguments to the find method while still leveraging the generic List's built in find features (namely iterating the collection for you)



This is where anonymous delegates come in to play. Using the same person list we loaded above, we can eliminate our find method all together and do this instead:



static void Main(string[] args)
{
List<Person> personList = LoadPersonList();
Person found = personList.Find( delegate (Person p) { return p.FirstName == "Bob";});
}


Neat huh?



To take it a little further if you want to be able to call a generic method passing the List and the value to search for (instead of writing the above for each time you need to find an item). You could do something like this :



static Person FindPersonByFirstName(string firstName, List<Person> list)
{
return list.Find(delegate(Person p) { return p.FirstName == firstName; });
}


With this implementation of the Find method the Main method from above would like this:



static void Main(string[] args)
{
List<Person> personList = LoadPersonList();
Person found = FindPersonByFirstName("Bob", personList);
}

This works for all of the methods attached List<T> and will simplify the mundane task implementing the guts of the various methods (FindAll,FindLast,Exists,etc..)

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home