A “custom” implementation of list can be found in this project. using System; // This is required for the exception. public class CList<T> { // A CList is … a Cell. private Cell first; // By default, a CList contains only an empty cell. public CList() { first = null; } // A Cell is itself two things: // - An element of data (of type T), // - Another cell, containing the next element of data. // We implement this using automatic properties: private class Cell { public T Data { get; set; } public Cell Next { get; set; } public Cell(T dataP, Cell nextP) { Data = dataP; Next = nextP; } } // A method to add a cell at the beginning // of the CList (to the left). // We call it AddF for "Add First". public void AddF(T dataP) { first = new Cell(dataP, first); } // A method to add a cell at the end // of the CList (to the right). // We call it AddL for "Add Last". public void AddL(T dataP) { if (first == null) AddF(dataP); else { Cell current = first; while (current.Next != null) // As long as the current Cell has a neighbour… { current = current.Next; // We move the current cell to this neighbour. } // When we are done, we can insert the cell. current.Next = new Cell(dataP, null); } } // We will actually frequently test if // a CList is empty, so we might // as well introduce a method for that: public bool IsEmpty() { return (first == null); } // Accessor for the size of the CList. public int Size { get { int size; if (IsEmpty()) { size = 0; } else { size = 1; Cell current = first; while (current.Next != null) // As long as the current Cell has a neighbour… { current = current.Next; // We move the current cell to this neighbour. size++; } } return size; } } // We can implement a ToString method // "the usual way", using a loop // similar to the one in AddL: // (But we make it very fancy, as // if we were drawing an array). public override string ToString() { string returned = ""; for (int i = 0; i < Size; i++) { returned += "————"; } returned += "\n| "; Cell current = first; while (current != null) { returned += $"{current.Data} | "; current = current.Next; } returned += "\n"; for (int i = 0; i < Size; i++) { returned += "————"; } return returned; } // Method to obtain the nth element if it exists. public T Access(int index) { if (index >= Size) { throw new IndexOutOfRangeException(); } else // Some IDE will flag this "else" as redundant. { int counter = 0; Cell current = first; while (counter < index) { current = current.Next; counter++; } return current.Data; } } /* * We can write four methods to * remove elements from a CList. * - One that clears it entirely, * - One that removes the first cell, * - One that removes the last cell, * - One that removes the nth cell, if it exists, */ public void Clear() { first = null; } public void RemoveF() { if (!IsEmpty()) first = first.Next; } public void RemoveL() { if (!IsEmpty()) { if (first.Next == null) { RemoveF(); } else { Cell current = first; while (current.Next != null && current.Next.Next != null) { current = current.Next; } current.Next = null; } } } // Method to remove the nth element if it exists. public void RemoveI(int index) { if (index > Size) { throw new IndexOutOfRangeException(); } else // Some IDE will flag this "else" as redundant. { int counter = 0; Cell current = first; while (counter < index - 1) { current = current.Next; counter++; } current.Next = current.Next.Next; } } // Method to obtain the largest // number of consecutive values // dataP. public int CountSuccessive(T dataP) { int cCount = 0; int mCount = 0; Cell cCell = first; while (cCell != null) { if (cCell.Data.Equals(dataP)) { cCount++; } else { if (cCount > mCount) { mCount = cCount; } cCount = 0; } cCell = cCell.Next; } if (cCount > mCount) { mCount = cCount; } return mCount; } // Method to remove at a particular index public void RemoveAt(int index) { if (index >= 0 && index < Size) { if (index == 0) RemoveF(); else if (index == (Size - 1)) RemoveL(); else { Cell cCell = first; for (int i = 0; i < index - 1; i++) { cCell = cCell.Next; } cCell.Next = cCell.Next.Next; } } else throw new ArgumentOutOfRangeException(); } // Method to reverse a list public void Reverse() { Cell cCell = first; Cell previous = null; Cell next; while (cCell != null) { next = cCell.Next; cCell.Next = previous; previous = cCell; cCell = next; } first = previous; } // Method to look for a specific value (recursively) public bool Find(T dataP) { return Find(first, dataP); } private bool Find (Cell cCell, T dataP) { if (cCell == null) return false; else if (cCell.Data.Equals(dataP)) return true; else if (cCell.Next == null) return false; else return Find(cCell.Next, dataP); } // Method to obtain the last index // of dataP. public int LastIndexOf(T dataP) { int index = 0, lastIndex = -1; Cell cCell = first; while (cCell != null) { if (cCell.Data.Equals(dataP)) { lastIndex = index; } index++; cCell = cCell.Next; } return lastIndex; } // Recursive method to obtain the // frequency of dataP public double Frequency(T dataP) { if (Size == 0) throw new ArgumentNullException("The list is empty."); else return Count(dataP, first) / (double)Size; } private int Count(T dataP, Cell pTmp) { if (pTmp == null) return 0; else if (pTmp.Data.Equals(dataP)) return 1 + Count(dataP, pTmp.Next); else return 0 + Count(dataP, pTmp.Next); } } (Download this code){.download-button}