Page 1 of 1

Create A Strongly Typed Collection In C#

#1 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1637
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Posted 13 October 2007 - 10:10 PM

Welcome to my tutorial on Creating a Strongly Typed Collection C#. In this tutorial we will be looking at creating your own type safe collection. Lets say you want to create a collection of IP addresses, one that accepts only the type of IPAddress, to do this you need to create a class that inherits from CollectionBase Class.

This class gives you all the functionality you need for your strongly typed collection. Before we get to that collection, we need to define an IPAddress collection that you strongly types collection will accept. In this collection we will implement the IComparable Interface. Here is the official definition of IComparable:

"Defines a generalized comparison method that a value type or class implements to create a type-specific comparison method."

The IComparable Interface has but a single method, CompareTo. CompareTo gives us the ability, before adding anything to our strongly typed collection, to check the object being added and ensure its of the right type, if it isn't the compiler will generate a compile time error, rather than a run-time error. Catching mistakes like that at compile-time means our application isn't going to die if someone tries to add an object to our collection that it isn't mean to hold.

So now we will create our IPAddress Type for our collection. The first thing we need to add to our class, as always, is a reference to the Namespace's we will be using. We will need a reference to the System Namespace and the System.Collections Namespace. The System Namespace gives us access to the IComparable Interface, while the System.Collections Namespace gives us access to the CollectionBase Class. So now our Namespace's:

using System;
using System.Collections;



To make referencing our strongly typed collection easier, I suggest adding a Namespace to your class, as I have done here

namespace PC
{

}



I chose PC simply because I am PsychoCoder, you can have whatever Namespace you wish to have. Since we implement an Interface in this class our signature will look somewhat different. The signature for our IPAddress class will look like this:

public class IPAddress : IComparable
{

}



Next we need our global variables, which will be tied to ths public properties of the class. Since this is a simple class, used for the IPAddress type, we only have 2 variables, one for the ip itself and one for the host name:

private string _address;
private string _host;



Next we will declare our public properties, these will be accessible from outside our class because we declare them as public. Properties are natural extensions of data fields and they allow us to control access to the classes members. A read/write property, which we use in our class, consists of 2 parts:
  • get: Used to return the value of this data field once it has been written to
  • set: Used to write a value to this data field

A read only property only has 1 part, the get part, since it cannot be written to from outside the class its contained in. Our properties are read/write, one for the ip address itself, the other for the host name:

#region Properties
/// <summary>
/// Property to hold the IP address
/// </summary>
public string Address
{
	//return the value of the property
	get { return _address; }
	//set the value of the property
	set { _address = value; }
}

/// <summary>
/// property to hold the host name
/// </summary>
public string Host
{
	//return the value of the property
	get { return_host; }
	//set the value of the property
	set { _host = value; }
}
#endregion



NOTE: I always place areas of my classes in #region ... #endregion sections for ease of finding items when searching through my classes.

Next, if we want to be to access our class (initialize) from outside the class itself, we need Constructors. Constructors are the code that is executed once the object is initialized. In this class we have 2 constructors, the first one is a base constructor that sets our 2 variables, thus our properties, to an empty string. The second constructor utilizes overloading, which means we can have a different set parameters in each constructor. Here are our constructors:

#region Constructors
public IPAddress()
{
	_address = string.Empty;
	_host = string.Empty;
}

public IPAddress(string address,string host)
{
	_address = address;
	_host = host;
}
#endregion



When we implement from IComparable you have to have a CompareTo method, which happens to be the only method of the IComparable Interface. The CompareTo Method is what allows us to compare each object before it is added to our strongly typed collection. It allows us to ensure the type being added is of the PAddress type. When using CompareTo 3 possible values are returned
  • > 0
  • 0
  • < 0

When using CompareTo we always want a return value of 0 (zero), which means we are adding the proper type to our collection. Here is our CompareTo method:

#region IComparable Members
public int CompareTo(object obj)
{
	//check to see if the type being passed is
	//of the IPAddress type
	if (!(obj is IPAddress))
	{
		//since its not we need to throw
		//an ArgumentException
		throw new ArgumentException("Object provided is of the wrong type");
	}
	//convert the object being passed to the type IPAddress
	IPAddress addr = (IPAddress)obj;
	//execute CompareTo and set its return
	//value to our int variable
	int cmpl = this.Address.CompareTo(addr.Address);
	//check the value of the CompareTo method
	if (!(cmpl == 0))
	{
		//not equal to zero, meaning it isnt the
		//right object
		return cmpl;
	}
	return this.Address.CompareTo(addr.Address);
}



Thats the end of our "type" collection. Now that we have finished our type, we need to create our strongly typed collection. In this collection we inherit from the CollectionBase Class, which gives us more functionality than your average collection.

CollectionBase implements the IList Interface, which gives us access to methods such as
  • Add: Add an item to the current collection
  • Contains: Used to check if the collection contains the current element before adding it
  • Remove: Remove a specified item from a strongly typed collection
  • Insert: Insert an item into the collection at the specified index

CollectionBase gives us access to methods such as:
  • CopyTo: Copy an entire collection to a one-dimensional array at the specified index
  • RemoveAt: Remove an item from the collection at the specified index
.

First, since we inherit from CollectionBase we need to specify that in our signature:

public class AddressList : CollectionBase
{

}



For this collection class we need a Constructor as well, but the constructor for this class will be empty, it's just there to allow us to initialize our collection:

#region Constructors
public AddressList()
{

}
#endregion



Next, instead of properties in this class, like the class that defines our type, we use an Indexer. As the definition states, Indexers allow us to index a class in the same way an Array is indexed. The only difference between a property and an indexer is an indexer accepts parameters. Our indexer looks like this:

#region Indexer
/// <summary>
/// Indexer for our collection
/// </summary>
/// <param name="idx">Index of the collection</param>
/// <returns>IPAddress at that index</returns>
public IPAddress this[int idx]
{
	get
	{
		//returns the IPAddress at the specified index
		return (IPAddress)this.InnerList[idx];
	}

}



Now we get to the CollectionBase/IList Methods. In order to add things to our strongly typed collection we need an Add Method. This method is a single line of code, and it simply adds the item to the collection in the next available index:

#region Add
/// <summary>
/// Method for adding an IPAddress to the list
/// </summary>
/// <param name="addr">IPAddress to add</param>
public void Add(IPAddress addr)
{
	//add this item to the list
	this.InnerList.Add(addr);
}
#endregion



Since this is a strongly typed collection, we don't want to allow duplicate entries. Since we're inheriting from CollectionBase this offers us the functionality to search our collection, and having duplicate entries can cause some unwanted results, so we add a Contains method which checks the items of our collection to see if what we're wanting to add already exists in the collection. In this method, we enumerate through each item in the collection, checking if the address/host name combination being added exists anywhere in our collection, if it does we return true, otherwise we return false:

#region Contains
/// <summary>
/// Method to check and see if the
/// items is in the list
/// </summary>
/// <param name="addr">Item to check</param>
/// <returns>true/false</returns>
public bool Contains(IPAddress addr)
{
	//loop through all the items in the list
	foreach (IPAddress item in this.InnerList)
	{
		//check to see if its in the list
		//returning 0 means its in the list
		if (item.CompareTo(addr) == 0)
		{
			//return true
			return true;
		}
	}
	//not in the list return false
	return false;
}
#endregion



Now lets say you want to remove an item from your strongly typed collection. inheriting from CollectionBase, which implementes the IList Interface, gives us access to the IList's Remove Method, so we take advantage of that:

#region Remove
/// <summary>
/// Method for removing a specified IPAddress fromthe list
/// </summary>
/// <param name="ip">IPAddress to remove</param>
public void Remove(IPAddress ip)
{
	//check to see if the list contains this
	//item, if it doesnt exit the procedure
	if (!this.Contains(ip)) return;
	//declare a counter variable
	int i;
	//loop through all the items in the list
	for (i = 0; i <= this.InnerList.Count; i++)
	{
		//create an instance of the IPAddress
		IPAddress item = (IPAddress)this.InnerList[i];
		//now check to see if its in the list
		if ((item.CompareTo(ip) == 0))
		{
			//if its in the list remove it
			this.RemoveAt(i);
			return;
		}
	}
}
#endregion



One method that's accessible when inheriting from CollectionBase, and is almost always overlooked is the OnValidate Method. This method is very important when creating a strongly typed collection. CollectionBase, as stated before, implements the IList Interface, which means that a user can cast your object to an IList object then call any of the methods in the IList Interface. This gives the user the opportunity to insert incorrect types into your collection. This method should throw an ArgumentException if the type being passed isn't of the type we're expecting:

#region OnValidate
protected override void OnValidate(object value)
{
	//first validate the object
	base.OnValidate(value);
	//if the object isnt of the correct type
	//throw an ArgumentException
	if (!(value is IPAddress))
	{
		throw new ArgumentException("Collection only supports items of type IPAddress");
	}
}
#endregion



There is of course much more than can be accomplished when inheriting from IComparable and CollectionBase, but I touched on the most commonly used methods, the ones that you will use continually in your strongly typed collections. Inheriting from base classes and implementing base interfaces available to you in the .Net Framework will make you a much more powerful programmer in the long run, and give you a far better understanding of the power this framework contains, and of Object Oriented Programming.

I am including for download, the class that I created for writing this tutorial. Remember, since the code is under the GNU - General Public License you may modify, use and distribute as you see fit, but the license header must stay intact. That is the end of this tutorial on Creating a Strongly Typed Collection in C#. I hope you found this tutorial useful and informative. Thanks for reading.

Happy Coding :)

Attached File  IPAddress.zip (2.03K)
Number of downloads: 983

Is This A Good Question/Topic? 0
  • +

Replies To: Create A Strongly Typed Collection In C#

#2 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5746
  • View blog
  • Posts: 12,555
  • Joined: 16-October 07

Posted 16 October 2007 - 08:14 AM

Nice tutorial, typed Collections are a cornerstone of good OO design. In fact, everyone had to rewrite collection classes so often that a new solution was proposed and has been out for some time, Generics.

Your entire implementation of AddressList can implemented with just this:

public class AddressList : System.Collections.Generic.List<IPAddress> {
	public AddressList() {}
}



However, I don't think IComparable means what you think it means. It's basically for sorting, where here you're using it for identity / equality. You seem to really want to override the base method shared by all classes, Equals.

I don't like throwing Exceptions for invalid objects, for IComparable I'd just give a -1 to bad requests. For Equals, you send a false if you get something you don't like. Here's an alternative IPAddress class for the Generic list above.

public class IPAddress : IComparable {
	private string address;
	private string host;

	public IPAddress() { }

	public IPAddress(string address, string host) {
		this.address = address;
		this.host = host;
	}

	public string Address {
		get { return this.address; }
		set { this.address = value; }
	}

	public string Host {
		get { return this.host; }
		set { this.host = value; }
	}

	public int CompareTo(object obj) {
		if (obj==null) { return -1; }
		if (!(obj is IPAddress)) { return -1; }
		return this.address.CompareTo(((IPAddress)obj).address);
	}

	public override bool Equals(object obj) {
		if (!(obj is IPAddress)) { return false; }
		return this.address.Equals(((IPAddress)obj).address);
	}

	// This needs to implemented for Equals to work.
	public override int GetHashCode() {
		return this.address.GetHashCode();
	}

}



I'd love to see you do a Generics tutorial. After doing it the old object boxing way, I'm sure you'll really enjoy the .NET 2.0 version.
Was This Post Helpful? 0
  • +
  • -

#3 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1637
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Posted 17 October 2007 - 01:01 PM

Once I am done with my series on Multi-Threading I plan on writing one on Generics. I wrote one for VB.Net now its time for C#.

By the way, I program exclusively in 2.0, and have since it came out.

And as far as your comment about me not knowing what IComparable is used for, you couldn't be further from the truth, its actually an excellent way to ensure type safety. If you do some extensive research on strongly typed collections, which I did before writing my first one, most use IComparable for just that purpose.

Thanks for the comments though :)
Was This Post Helpful? 0
  • +
  • -

#4 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5746
  • View blog
  • Posts: 12,555
  • Joined: 16-October 07

Posted 17 October 2007 - 04:15 PM

View PostPsychoCoder, on 17 Oct, 2007 - 04:01 PM, said:

most use IComparable for just that purpose. for C#.


I'm afraid I find this to be a highly questionable claim.

Consider how Microsoft does this. Notice that the typed methods tend to be overridden with simple code, like this:

public bool Contains( Int16 value )  {
	return( List.Contains( value ) );
}


A Typed Collection should really only be spending extra code on OnValidate and some typed methods. Your code has to work extra hard because the objects in the collection are functionally invalid for use in a collection.

Consider this code:

IPAddress [] testList = new IPAddress[] {  

	new IPAddress("192.168.1.2","larry"),

	new IPAddress("192.168.1.3","curly"),

	new IPAddress("192.168.1.4","moe")

};


IPAddress testItem = new IPAddress("192.168.1.3","shemp");



ArrayList aList = new ArrayList(testList);

Console.WriteLine(aList.Contains(testItem));


AddressList taList = new AddressList();

foreach(IPAddress item in testList) {taList.Add(item); }



Console.WriteLine(taList.Contains(testItem));



Results:
False
True


If Equals was overridden, the first case would be true as well and all that extra code would be unnecessary.

I wouldn't normally go to such exhaustive effort to point this out. After all, it's your code and if it works for you, great. However, if you're offering it up as a tutorial, for others to learn from and perhaps emulate, I'd think you'd be more open to an obvious criticism. If the methodology has a reasoned purpose that I, the student, do not see, I'd hope for a greater explanation than citing some bogus "extensive research."

Again, I am sorry to be so critical. I'll say no more on this.

This post has been edited by baavgai: 17 October 2007 - 04:17 PM

Was This Post Helpful? 0
  • +
  • -

#5 b as it is  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 20-October 09

Posted 20 October 2009 - 09:32 PM

[quote name='baavgai' date='17 Oct, 2007 - 03:15 PM' post='268488']

View PostPsychoCoder, on 17 Oct, 2007 - 04:01 PM, said:

most use IComparable for just that purpose. for C#.

Quote

thanks baavgai!but i am beginer and i want defination of collection.I don't know what is collection.so please help me to understand collection.

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1