Implementing Custom Data Bindable Classes: IList
Pages: 1, 2, 3
Let's implement the IList properties first. Two of them, IsReadOnly and
IsFixedSize, are already done because the IDE has set them to return false. Since we want the list
to grow and shrink with new values, this default setting is fine. But we do need to implement
the Contains property, which should return true if any node in the list contains the
value that is passed in as a parameter:
public bool Contains(object value)
{
bool containsNode = false;
if (head != null)
{
Node tempNode = head;
if (tempNode.Item == value)
{
containsNode
= true;
}
else
{
for (int i = 0; i < count; ++i)
{
tempNode = tempNode.Next;
if (tempNode.Item == value)
{
containsNode = true;
}
}
}
}
return containsNode;
}
We also have to implement an indexer so that elements can be accessed in the list by their index position:
public object this[int index]
{
get
{
Node temp = head;
if (index > -1 && index < count)
{
if (index !=
0)
{
for(int i = 0; i < index; ++i)
{
temp = temp.Next;
}
}
}
return temp.Item;
}
set
{
Node temp = head;
if (index > -1 && index < count)
{
if (index !=
0)
{
for(int i = 0; i < index; ++i)
{
temp = temp.Next;
}
}
temp.Item =
value;
}
}
}
That takes care of the properties for IList. Now let's implement
its methods. The first method is Add, which accepts any object
as its value and will insert a new node at the end of the list to store that
value. It should return the index position of the new node:
public int Add(object value)
{
if (IsFixedSize)
{
throw new NotSupportedException("List is a fixed size.");
}
if (head != null)
{
Node tempNode = head;
Node newNode = new Node();
newNode.Item = value;
// add Item to end of list
for(int i = 0; i < count - 1; ++i)
{
tempNode = tempNode.Next;
}
tempNode.Next = newNode;
}
else
{
head = new Node();
head.Item = value;
}
count++;
return count;
}
The Clear method removes all nodes from the list:
public void Clear()
{
if (IsReadOnly)
{
throw new NotSupportedException("List is read-only.");
}
if (head != null)
{
Node prevNode;
Node tempNode = head;
for (int i = 0; i < count; ++i)
{
prevNode = tempNode;
tempNode = tempNode.Next;
prevNode = null;
}
}
}
The IndexOf method takes a value as a parameter and searches the list for
that value. If found, it returns the index number for that node, otherwise it
returns -1:
public int IndexOf(object value)
{
int idx = -1;
Node temp = head;
for(int i = 0; i < count; ++i)
{
if (temp.Item == value)
{
idx = i;
}
}
return idx;
}
The Insert method is similar to the Add method except that it can insert a
new node anywhere in the list. That requires a little more work to implement:
void System.Collections.IList.Insert(int index, object value)
{
if ((IsReadOnly) || (IsFixedSize))
{
throw new NotSupportedException("List is either " +
"read-only or a fixed size.");
}
if (index > -1 && index < count)
{
// insert at position index
if (head != null)
{
Node currNode
= head;
// get to
index position
for (int i = 0; i == index; ++i)
{
currNode = currNode.Next;
}
Node nextNode = currNode.Next;
// create new node and assign value
Node newNode = new Node();
newNode.Item = value;
// insert new node between curr and Next
currNode.Next = newNode;
newNode.Next = nextNode;
}
else
{
// insert in first position as the head
head = new Node();
head.Item = value;
}
}
else
{
throw new
ArgumentOutOfRangeException("Index is out of range.");
}
count++;
}
The last two methods in the IList interface are Remove
and RemoveAt, which are similar in that both remove a node from the
list. The difference is that Remove will search the list for a passed-in
value and remove the first node that matches that value:
public void Remove(object value)
{
if (head != null)
{
Node prevNode = head;
Node tempNode = head;
if (tempNode.Item == value)
{
head = null;
}
else
{
for (int i = 0; i < count; ++i)
{
tempNode = tempNode.Next;
if (tempNode.Item == value)
{
// point previous node to Next node
Node nextNode = tempNode.Next;
prevNode.Next = nextNode;
tempNode = null;
count--;
return;
}
else
{
prevNode = tempNode;
}
}
}
}
else
{
throw new Exception("List is empty.");
}
}
RemoveAt merely gets the node from the list at the location specified by
the passed-in index position. In both cases, if the node to be removed is somewhere in
the middle of the list, then we must link the previous node to the
node after the one to be removed. If we didn't do that we'd break the chain and lose the
integrity of our list:
public void RemoveAt(int index)
{
if ((IsReadOnly) || (IsFixedSize))
{
throw new NotSupportedException("List " +
"is either read-only or a fixed size.");
}
if (index > -1 && index < count)
{
if (head != null)
{
// get to index position
Node prevNode = head;
Node tempNode = head;
if (index != 0)
{
for (int i = 0; i < index; ++i)
{
prevNode = tempNode;
tempNode = tempNode.Next;
}
prevNode.Next = tempNode.Next;
tempNode = null;
}
else
{
head = tempNode.Next;
}
count--;
}
else
{
throw new Exception("List is empty.");
}
}
else
{
throw new
ArgumentOutOfRangeException("Index is out of range.");
}
}

