Friday, February 1, 2008

Using List<T>.Sort() and Find() by delegate

Keywords: C# Generic, List<T> class, Find, Sort, Comparison, Predicate


In List<T> class, Sort() method will perform to sort the list item. For List<string>, you can call Sort() method without argument. It will perform well. But if you sort List<Person> by using the same way, One error happened.


Why? Because the .Net Runtiime know how to compare toow string object, but it do not know how to compare two Person objects. In this situation, you will need define your comparison method. There is the same problem when using List.Find() method.


How to use List<T>.Sort()?


Refer to the List<T>.Sort source code. There are 4 override method, one of them is as follows,


public void Sort( Comparison <T> comparison);


And then refer to Comparison<T>, In System namespace


public delegate int Comparison <T>(T x, T y);


Ok, it is time to write code. You can define a static comparison method in PersonHelper class(one helper class for Person), it must follow the Comparison <T> delegate signature.


How to use List<T>.Find() ?


Look at the arguement of the Find(). This is also a delegate.


public T Find( Predicate <T> match);


and then refer to Predicate<T>, In System namespace


public delegate bool Predicate <T>(T obj);


We need pass one delegate object to Find() method, e.g CompareByNamePredicate. But the ComparePredicate() have only one argument, which is the enumerator of Generic List dictionary. How we specify the find condition, e.g, "TOM" for one Person name. We can encapsulate the CompareByNamePredicate method in one helper class. and we let one private variable of the helper class object to hold "TOM". Then we have Predicate method and Find condition. The subsequence is simple.


If you want use Helper class to achieve it, you can use anonymous delegate method. please refer to http://blogs.msdn.com/devdev/archive/2006/06/30/652802.aspx


The following code demo how to use helper class and anonymous delegate to approach the Find() and Sort() function.




/// <summary>
/// Main class, in this class we call the List.Find() and List.Sort().
/// </summary>
public partial class Form1 : Form
{
List<Person> m_ListPerson = new List<Person>();

public Form1()
{
InitializeComponent();
m_ListPerson.Add(new Person("c1"));
m_ListPerson.Add(new Person("a1"));
m_ListPerson.Add(new Person("TOM"));
}


/// <summary>
/// Use named delegate method to approach the sort function
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSort_Click(object sender, EventArgs e)
{
m_ListPerson.Sort(PersonHelper.CompareByName);
listBox1.Items.Clear();
foreach (Person p in m_ListPerson)
{
listBox1.Items.Add(p.FirstName);
}
}


/// <summary>
/// use anonymous delegate to approach the sort function
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSortByDelegate_Click(object sender, EventArgs e)
{
//public delegate int Comparison<T>(T x, T y);
m_ListPerson.Sort(delegate(Person p1, Person p2) { return string.Compare(p1.FirstName, p2.FirstName); });
listBox1.Items.Clear();
foreach (Person p in m_ListPerson)
{
listBox1.Items.Add(p.FirstName);
}
}


/// <summary>
/// Use Helper class and named method to approach the find function
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonFind_Click(object sender, EventArgs e)
{
PersonHelper personHelper = new PersonHelper();
personHelper.FirstName = "TOM";
Person person = m_ListPerson.Find(personHelper.CompareByNamePredicate);
listBox1.Items.Clear();
if (person != null)
{
listBox1.Items.Add(person.FirstName);
}
}

/// <summary>
/// use anonymous delegate to approach the find function
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonFindByDelegate_Click(object sender, EventArgs e)
{
///public delegate bool Predicate<T>(T obj);
Person person = m_ListPerson.Find(delegate(Person p) { return string.Equals(p.FirstName, "TOM"); });
listBox1.Items.Clear();
if (person != null)
{
listBox1.Items.Add(person.FirstName);
}
}
}


/// <summary>
/// This is a simple Class
/// </summary>
public class Person
{
private string m_FirstName;

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

public Person(string firstName)
{
this.m_FirstName = firstName;
}

}


/// <summary>
/// This is helper class, which provides sort and find capablity for Generic Collection Class
/// </summary>
public class PersonHelper
{

/// <summary>
/// implement the compare method, and it has the same signature with delegate Comparison
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
//public delegate int Comparison<T>(T x, T y);
public static int CompareByName(Person p1, Person p2)
{
return string.Compare(p1.FirstName, p2.FirstName);
}

string m_FirstName;

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


/// <summary>
/// /// implement the predicate method in order to find one person, and it has the same signature with delegate Predicate
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
///public delegate bool Predicate<T>(T obj);
public bool CompareByNamePredicate(Person p)
{
return string.Equals(p.FirstName, m_FirstName);
}
}

No comments: