|
A database is collection of values. To create these values,
you can use a collection class. The .NET Framework provides a very impressive library of
interfaces and collection classes. The built-in interfaces of the .NET Framework lay a
foundation that other classes use to implement and customize the desired
functionality.
The .NET Framework supports collections in various
namespaces. While the System.Collections namespace provides regular
collection classes, the System.Collections.Generic namespace contains the
equivalent generic classes.
|
Accessories for Collections
|
|
One the most routines operations performed on a database consists of reviewing its
values. To assist you with this, the .NET Framework
provides the IEnumerator and the IEnumerable interfaces that are defined in the System.Collections namespace.
Their generic equivalences can be found in the System.Collections.Generic
namespace. After implementing these interfaces, you can use the foreach operator to
visit each value of the database.
To implement the System.Collections.IEnumerator interface, you must
derive a class from it. Then, you must define the Reset(), the MoveNext()
methods, and the Current property. Here is an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for Enumerator
/// </summary>
public class Enumerator : IEnumerator
{
private string[] names;
private int cur;
public Enumerator(string[] list)
{
this.names = list;
cur = -1;
}
public Object Current
{
get { return names[cur]; }
}
public void Reset()
{
cur = -1;
}
public bool MoveNext()
{
cur++;
if (cur < names.Length)
return true;
else
return false;
}
}
To implement the System.Collections.IEnumerable interface, you must
derive a class from it. When implementing the class, you must define an
accessory method and the GetEnumerator() method that returns an IEnumerator
object. Here is an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for Enumerable
/// </summary>
public class Enumerable : IEnumerable
{
private string[] names;
public Enumerable()
{
}
public void Identify(string[] values)
{
names = values;
for (int i = 0; i < values.Length; i++)
names[i] = values[i];
}
public IEnumerator GetEnumerator()
{
return new Enumerator(names);
}
}
Once you have implemented the interfaces, you can use foreach. Here is an example:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Exercise
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var FullNames = new string[8];
FullNames[0] = "Gertrude Monay";
FullNames[1] = "Paul Bertrand Yamaguchi";
FullNames[2] = "Hermine Ngaleu";
FullNames[3] = "Francine Mukoko";
FullNames[4] = "Joseph Walters";
FullNames[5] = "Patricia Katts";
FullNames[6] = "Helen Cranston";
FullNames[7] = "Paul Motto";
var coll = new Enumerable();
coll.Identify(FullNames);
foreach (string s in coll)
lbxNames.Items.Add(s);
}
}
}
While the IEnumerator and the IEnumerable interfaces serve
as valuable accessories that allow a collection class to support enumeration, to actually
create a collection class, there are other interfaces you can use to
implement the functionality you want for your collection.
When you want to use
a collection in your application, you may first check what classes are
available in the .NET Framework. If you don't find a suitable class, you
can create your own that implements one or more interfaces. As it happens,
the .NET Framework ships with many of them and your next step is to choose
which one you prefer. Some of the most commonly used interfaces are
- System.Collections.IComparer and System.Collections.Generic.IComparer:
If you derive a class from this interface, you can
define how two objects would be compared for similarity or difference
- System.Collections.IDictionary and System.Collections.Generic.IDictionary: This interface is used to create a collection class
where each item is made of a key=value combination
The ICollection Interface
|
|
One of
the primary pieces of information you should provide about the values in a
database is the number of values that a list is (currently) holding. When creating a collection
class, to prepare it to provide this valuable information, you can (should)
implement an interface named ICollection. The ICollection
interface is defined in the System.Collections namespace while its
equivalent of the same name is defined in the System.Collections.Generic
namespace. This means
that, if you are creating a class that implements it, you should include this
namespace in the file. Here is an example for the System.Collections.ICollection
interface:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for Collection
/// </summary>
public class Collection : ICollection
{
public Collection()
{
}
}
To assist you with keeping track of the number of items in a
collection, the ICollection interface is equipped with a property named Count,
which you must implement. To do this, you can create a private member variable
that will actually keep a count of the number of items. The Count property can
then be used to communicate this information to the clients of the class. Here is
an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for Collection
/// </summary>
public class Collection : ICollection
{
private int NumberOfBooks;
public Collection()
{
NumberOfBooks = 0;
}
public virtual int Count
{
get { return NumberOfBooks; }
}
}
The ICollection interface also allows its implementer
to copy some of its items to an array. To provide this functionality, the
interface is equipped with a method named CopyTo, which you must
implement. The syntax of this
method is:
void CopyTo(Array array, int index);
This method takes two arguments. The first argument is the
array that will receive the items. The second argument is the index of the item
from where the copying operation will begin. Here is an example:
public virtual void CopyTo(Array items, int index)
{
string[] bks = new string[Count];
for (int i = 0; i < Count; i++)
bks[i] = books[i];
items = bks;
}
If you create a collection class, you can provide the
ability to enumerate its items. When this is done, some time to time, you will
want to identify or to know what item is currently being accessed. In case other
collection classes are using the same function at the time you are accessing
this information, you should have an object that is responsible for synchronizing the
collection. To do this in your ICollection-based class, you must
implement a property named SyncRoot. This property must return an Object
object. Here is an example:
public virtual object SyncRoot
{
get { return this; }
}
Besides the ability to specify the number of items in a
collection, a class that implements the ICollection interface must
retrieve a value that indicates whether its item is synchronized. To give this
information, you must implement a Boolean property named IsSynchronized.
Here is an example:
public virtual bool IsSynchronized
{
get { return false; }
}
System.Collections.ICollection (and System.Collections.Generic.ICollection)
extends the IEnumerable interface.
This means that you should be able to use foreach in your ICollection-based
class but you must create the functionality yourself, which is done by
implementing the GetEnumerator() method. Even if you don't want to
support this feature, you still must provide at least a skeleton for this
method. Here is an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for Collection
/// </summary>
public class Collection : ICollection
{
private int NumberOfBooks;
private string[] books;
public Collection()
{
NumberOfBooks = 0;
books = new string[5];
}
public virtual int Count
{
get { return NumberOfBooks; }
}
public virtual void CopyTo(Array items, int index)
{
string[] bks = new string[Count];
for (int i = 0; i < Count; i++)
bks[i] = books[i];
items = bks;
}
public virtual object SyncRoot
{
get { return this; }
}
public virtual bool IsSynchronized
{
get { return false; }
}
public IEnumerator GetEnumerator()
{
return null;
}
}
While it provides the minimum functionality of a collection,
the System.Collections.ICollection (and the System.Collections.Generic.ICollection)
interface is not equipped to perform the regular
operations of a collection class, such as adding, retrieving, or deleting items
from a set.
To assist you with creating a collection class as complete as possible, the
.NET Framework provides an interface named IList. The IList
interface is defined in the System.Collections namespace and its
equivalent of the same name is defined in the System.Collections.Generic
namespace. The interface is equipped with the methods necessary to add, insert, delete, or
retrieve items from a collection. Because the functionalities of these methods
may not suit you, to use these features, you must create a class that implements
them.
As mentioned above, to create a collection, you can derive
it from the IList
interface. Here is an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for BookList
/// </summary>
public class BookList : IList
{
public BookList()
{
}
}
This System.Collections.IList interface is declared as follows:
public interface IList : ICollection, IEnumerable
This System.Collections.Generic.IList interface is declared as follows:
public interface IList<T> : ICollection<T>,
IEnumerable<T>,
IEnumerable
This means that the IList interface extends both the ICollection
and the IEnumerable interfaces. This also implies that you must implement
the members of these parent interfaces. In other words, you must implement the
Count property, the SyncRoot property, the IsSynchronized
property, and the CopyTo() method of the ICollection interface.
From what we learned with ICollection, here are examples of implementing these
members for the System.Collections.IList interface:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for BookList
/// </summary>
public class BookList : IList
{
private int counter;
private object[] objs;
public BookList()
{
counter = 0;
objs = new object[5];
}
public virtual int Count
{
get { return counter; }
}
public virtual bool IsSynchronized
{
get { return false; }
}
public virtual object SyncRoot
{
get { return this; }
}
public virtual void CopyTo(Array ary, int index)
{
}
}
You must also implement the System.Collections.GetEnumerator()
(or the System.Collections.Generic.GetEnumerator()) method of
the System.Collections.IEnumerable (or of the System.Collections.Generic.IEnumerable) interface. If you
do not have time to completely implement
it, you can simply return null. Here is an example for the System.Collections.IList
interface:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for BookList
/// </summary>
public class BookList : IList
{
. . . No Change
public IEnumerator GetEnumerator()
{
return null;
}
}
When you add or insert values to a list, the Count
property grows.
If you create a list whose number of values must be constant,
the user cannot add values beyond the maximum allowed number. Therefore, before
adding a value, you can first check whether the collection has a fixed size or
not. To give you this information, the IList interface is equipped with
a Boolean read-only property named IsFxedSize. This property simply lets
the user know whether the collection has a fixed number of items.
Most databases are meant to receive new values. If you
want, you can create a list that cannot receive new values. To support
this, the IList interface is equipped with the Boolean IsReadOnly
property. If a list is read-only, it would prevent the clients from
adding items to it.
Populating the Collection
|
|
As it should be obvious, the primary operation to perform on a list is to
populate it with at least one value. To support this, the System.Collections.IList interface
is equipped with a method named
Add. Its syntax is:
int Add(object value);
This method takes one argument as the value to add to the
list. If your collection is an array, you can first check that there is
still enough room in the list to add a new item. In reality, this is never
an issue with the System.Collections.IList interface.
If the method succeeds with the addition, it returns the position where the
value was added in the list. This is usually the last position in the list.
Here is an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for BookList
/// </summary>
public class BookList : IList
{
. . . No Change
public virtual int Add(object value)
{
// Check whether there is still room in
// the array to add a new item
if (counter < objects.Length)
{
// Since there is room, put the new item to the end
objects[items] = value;
// increase the number of items
objects++;
// Return the index of the item that was added
return counter - 1;
} // Since the item could not be added, return a negative index
else
return -1;
}
}
When you call the System.Collections.IList.Add() method, it adds the
new value to the end of the list. Sometimes, you will want the new value to be
inserted somewhere inside the list. To support this operation, both the System.Collections.IList
and the System.Collections.Generic.IList interfaces provide a method named
Insert. The syntax of the System.Collections.IList.Insert() method
is:
void Insert(int index, object value);
The syntax of the System.Collections.Generic.IList.Insert()
method is:
void Insert(int index, T value);
This method takes two arguments. The second argument is the
value that will be inserted into the list. The argument must hold a valid
value. Because this method takes an Object object, if your collection is
using a different type of value, you may have to cast it to Object. The
first argument is the index of the item that will precede the new one.
Locating an Item in the Collection
|
|
This Default Item of the Collection
|
|
While using a list, various operations require that
you know the object you are currently accessing. To provide this operation, you
must create an indexed property. This property should take an index and return
the type of object that makes up the list. Here is an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for BookList
/// </summary>
public class BookList : IList
{
. . . No Change
public virtual object this[int index]
{
get { return objects[index]; }
set
{
objects[index] = value;
}
}
}
After creating this property, you can then access an item
using its index and applying the [] operator on its instance. Remember that if
you want to use foreach, you must appropriately implement the IEnumerable.GetEnumerator()
method.
Enumerating the Collection foreach Item
|
|
One of the most valuable features of the C# language is the ability
to use the foreach loop to enumerate the members of a collection. To make
this possible, you must implement the IEnumerator interface in your
collection class.
Checking the Existence of an Item
|
|
One of the routine operations you can perform on a list is
to find out whether it contains a certain value. To assist you with this
operation, the System.Collections.IList interface is equipped with a method named Contains. Its syntax is:
bool Contains(object value);
This method takes as argument the value to look for. If the
value is found in the list, the method returns true. If no value is found in the
collection, this method returns false.
Here is an example of implementing this method:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for BookList
/// </summary>
public class BookList : IList
{
. . . No Change
public bool Contains(object value)
{
for (int i = 0; i < Count; i++)
if (objects[i] == value)
return true;
return false;
}
}
This method calls the Equals() method of the objects
that make up the list to
find out whether the value argument exists in the collection. If this
method produces a wrong result, especially if you are using your own class to
represent the item, you may have to override your own Equals()
method.
Getting the Index of an Item
|
|
The System.Collections.IList.Contains() method is used to check whether a particular
value (already)
exists in the collection. If you know that a certain item exists but you don't know its index inside the list, the IList interface can
assist you through a method named IndexOf. Its syntax is:
int IndexOf(object value);
This method takes as argument the value to look for in the list. If the
value is found in the collection, the method returns its index. If there is no
value defined like that, the method returns -1. Here is an example of implementing
this method: