Generics, introduced to the .Net world with release of the 2.0 framework, provide a simple and clean method of maintaining type safety and efficiency in ways that strongly typed collection objects can't. The use of List<T> over ArrayList gives the user compile time type checking, and removes the need for Boxing and Casting when adding or removing objects from the collection.
That being said, nearly all of the documentation that I've seen on Generics use simple data types.. string, int, etc. What do you do when you want to take advantage of List<T>'s power, using your own object?
Let's start with a very basic Person object...
public class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private string _address;
public string Address
{
get { return _address; }
set { _address = value; }
}
}
So far, so good.. we have a name and address for this person. Let's get into contact with this person by providing a phone number. In this day and age, it's not uncommon for a person to have more than one phone number. I have 6 myself, Home, Cell1, Cell2, Office, Office Fax, Home Fax... let's create a simple phone class with some constructors for ease of use and phone type enum to go along with it..
public enum PhoneNumberType
{
Home
, Mobil
, Office
, Fax
}
public class Phone
{
public Phone() { }
public Phone(string number, PhoneNumberType numberType)
{
this.Number = number;
this.PhoneType = numberType;
}
private string _number;
public string Number
{
get { return _number; }
set { _number = value; }
}
private PhoneNumberType _phoneType;
public PhoneNumberType PhoneType
{
get { return _phoneType; }
set { _phoneType = value; }
}
}
Now, let's add the phone number collection to the Person object..
private List<Phone> _phoneNumbers = new List<Phone>();
public List<Phone> PhoneNumbers
{
get { return _phoneNumbers; }
set { _phoneNumbers = value; }
}
With our basic Person class built out, now we can start working on adding a method to add a new phone number to the list.. Since we're exposing the list to the world as a property, we could add a new phone object to the PhoneNumbers collection directly:
protected override void OnLoad(EventArgs e)
{
Person psn = new Person();
base.OnLoad(e);
Phone = new Phone("555-1212", PhoneNumberType.Home);
psn.PhoneNumbers.Add(Phone);
}
However, I would rather the Person class be responsible for adding phone numbers to it's collection, so we'll create an AddPhone method in the Person class:
public void AddPhone(string number, PhoneNumberType numberType)
{
_phoneNumbers.Add(new Phone(number, numberType));
}
Now it's just a matter of calling the AddPhone method on our Person object.. For this demonstration, I simply dragged a DataGridView onto my windows form. Since List<T> Implements IList, we cause us that as a Data Source to bind controls to. So let's bind the data grid to it..
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
psn.AddPhone("555-1212", PhoneNumberType.Home);
psn.AddPhone("555-1213", PhoneNumberType.Fax);
psn.AddPhone("555-1214", PhoneNumberType.Mobil);
psn.AddPhone("555-1215", PhoneNumberType.Office);
psn.AddPhone("555-1216", PhoneNumberType.Mobil);
dataGridView1.DataSource = psn.PhoneNumbers;
}
When we run our new application, we are pleasantly greeted with this:
Predicates and Anonymous methods...
As powerful as Generics are, they are still after all, Generic. That means that need to tell List<T> how to do certain things. Things like Exists(), Find(), FindAll, FindLast() and so on are determined by you defining a Predicate delegate. The exceptionally cool thing is, you can implement a predicate delegate as an anonymous method..
In our Person class, let's check to see if a number is in the list:
public bool NumberExists(string number)
{
return _phoneNumbers.Exists(delegate(Phone phn)
{
return phn.Number == number;
});
}
Now, tying all that together with a nice button, textbox, and label.. we can do something like this:
private void btnNumberExist_Click(object sender, EventArgs e)
{
if (psn.NumberExists(txtNumberExist.Text))
lblNumberExist.Text = "Number Exists!";
else
lblNumberExist.Text = "Number Does Not Exist!";
}
Which, when we run the application now, yields these results:
And:
