Basic Linked list class with some interfaces implemented to work with WPF UI elements
CLinkedList class, is a custom C# generic class implementing basic Doubly linked list operations with some extra features for ease of use and better integration with other .Net classes especially for WPF UI elements.
We chose the class to be generic so the list elements can be of any type chosen by the end user
public class CLinkedList<T> :
ICollection<T>,
IEquatable<CLinkedList<T>>,
ISerializable,
IDeserializationCallback,
INotifyCollectionChanged
where T : class {}
CLinkedList is a general-purpose linked list. It supports enumerators and implements the ICollection interface, consistent with other collection classes in the .NET Framework.
CLinkedList provides separate nodes of type CNode, so insertion and removal are O(1) operations.
The list maintains an internal count, getting the Count property is an O(1) operation.
Each node in a CLinkedList object is of the type CNode. Because the CLinkedList is doubly linked, each node points forward to the Next node and backward to the Previous node.
If the CLinkedList is empty, the First and Last properties contain null.
The CLinkedList class does not support chaining, splitting, cycles, or other features that can leave the list in an inconsistent state.
Class Members
Private Members:
_head: The first node in the list of type CNode<T>.
private CNode<T> _head;
_tail: The last node in the list of type CNode<T>.
private CNode<T> _tail;
_count: Nodes count in the list of type int.
private int _count;
_siInfo; Serialization data used while deserialization of the Type SerializationInfo.
private SerializationInfo _siInfo;
Public Members
First: Gets the first element in the list of type T.
public T First { get { return _head.Data; } }
Last: Gets the last element in the list of type T.
public T Last { get { return _tail.Data; } }
Count: Gets the number of nodes actually contained in the list.
public int Count { get { return _count; } }
IsReadOnly; Determines if the class is read only or not of type bool.
public bool IsReadOnly { get { return false; } }
Private Methods
AddNodeToEmptyList(CNode<T>)
Adds the specified node to an empty list.
private void AddNodeToEmptyList(CNode<T> newNode)
{
Debug.Assert(
_head == null && _count == 0,
"Can't use this function if the list is not empty!");
newNode.Next = null;
newNode.Prev = null;
_head = newNode;
_tail = _head;
}
InternalFind(int)
Finds the node at the specified index.
private CNode<T> InternalFind(int index)
{
if (index >= _count)
{
throw new ArgumentOutOfRangeException();
}
CNode<T> currentNode;
if (index < _count / 2)
{
currentNode = _head;
for (int i = 0; i < index; i++)
{
currentNode = currentNode.Next;
}
return currentNode;
}
else
{
currentNode = _tail;
for (int i = 0; i < _count - 1 - index; i++)
{
currentNode = currentNode.Prev;
}
return currentNode;
}
}
InternalFind(T)
Finds the node that contains the same specified data.
private CNode<T> InternalFind(T data)
{
CNode<T> tempNode = _head;
int index = 0;
EqualityComparer<T> c = EqualityComparer<T>.Default;
if (tempNode != null)
{
if (data != null)
{
do
{
tempNode.Index = index;
if (c.Equals(tempNode.Data, data))
{
return tempNode;
}
tempNode = tempNode.Next;
index++;
} while (tempNode != null);
}
}
return null;
}
CreateList(T[])
Initializes the list after deserialization from the specified array.
private void CreateList(T[] array)
{
for (int i = 0; i < array.Length; i++)
{
AddLast(array[i]);
}
}
Public Methods
this[int]
Gets or sets the element at the specified index.
public T this[int index]
{
get
{
return InternalFind(index).Data;
}
set
{
index += 1;
InternalFind(index).Data = value;
}
}
AddLast(T)
Adds a new node containing the specified value at the end of the list.
public void AddLast(T data)
{
CNode<T> newNode = new CNode<T>(data);
if (_head == null)
{
AddNodeToEmptyList(newNode);
}
else
{
_tail.Next = newNode;
newNode.Prev = _tail;
_tail = newNode;
}
_count++;
if (CollectionChanged != null)
{
CollectionChanged(this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
newNode.Data));
}
}
AddFirst(T)
Adds a new node containing the specified value at the start of the list.
public void AddFirst(T data)
{
CNode<T> newNode = new CNode<T>(data);
if (_head == null)
{
AddNodeToEmptyList(newNode);
}
else
{
newNode.Next = _head;
_head.Prev = newNode;
_head = newNode;
}
_count++;
if (CollectionChanged != null)
{
CollectionChanged(this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
newNode.Data));
}
}
RemoveLast()
Removes the node at the end of the list.
public void RemoveLast()
{
if (_head == null)
{
throw new InvalidOperationException(
"Can't remove the first element in an empty list!");
}
else
{
CNode<T> tNode = _tail;
if (_count == 1)
{
_head = null;
_tail = null;
}
else
{
_tail = _tail.Prev;
_tail.Next = null;
}
if (CollectionChanged != null)
{
CollectionChanged(this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove,
tNode.Data,
_count-1));
}
tNode.DeleteNode();
_count--;
}
}
RemoveFirst()
Removes the node at the start of the list.
public void RemoveFirst()
{
if (_head == null)
{
throw new InvalidOperationException(
"Can't remove the first element in an empty list!");
}
else
{
CNode<T> tNode = _head;
if (_count == 1)
{
_head = null;
_tail = null;
}
else
{
_head = _head.Next;
_head.Prev = null;
}
if (CollectionChanged != null)
{
CollectionChanged(this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove,
tNode.Data,
0));
}
tNode.DeleteNode();
_count--;
}
}
Remove(T)
Removes the first occurrence of the specified value from the list.