How does the implementation for Enumerator work in the Linked List class?

In the .NET resource reference, there’s a page on the LinkedList and LinkedListNode classes and in it, there’s a struct called Enumerator which implements the IEnumerator<T> and IEnumerator interfaces.

I decided to re-use pretty much all of the code for my own ForkableLinkedList<T> and ForkableLinkedListNode<T> classes, I get an error for the implementation of ForkableLinkedList<T>.GetEnumerator():

cannot convert from ‘CustomLinkedList.Models.ForkableLinkedList’ to ‘CustomLinkedList.Models.ForkableLinkedList’.

Here’s what my linked list looks like (I added a comment on the line where I get an error):

public class ForkableLinkedList<T> : ICollection<T>, System.Collections.ICollection, IReadOnlyCollection<T>     {         // This LinkedList is a doubly-Linked circular list.         internal ForkableLinkedListNode<T> head;         internal int count;         internal int version;         private Object _syncRoot;          public ForkableLinkedList()         {         }          public ForkableLinkedList(IEnumerable<T> collection)         {             if (collection == null)             {                 throw new ArgumentNullException("collection");             }              foreach (T item in collection)             {                 AddLast(item);             }         }          public int Count => count;          public ForkableLinkedListNode<T> First => head;          public ForkableLinkedListNode<T> Last => head == null ? null : head.prev;          bool ICollection<T>.IsReadOnly => false;          void ICollection<T>.Add(T value) => AddLast(value);          public ForkableLinkedListNode<T> AddAfter(ForkableLinkedListNode<T> node, T value)         {             ValidateNode(node);             ForkableLinkedListNode<T> result = new ForkableLinkedListNode<T>(node.list, value);             InternalInsertNodeBefore(node.next, result);             return result;         }          public void AddAfter(ForkableLinkedListNode<T> node, ForkableLinkedListNode<T> newNode)         {             ValidateNode(node);             ValidateNewNode(newNode);             InternalInsertNodeBefore(node.next, newNode);             newNode.list = this;         }          public ForkableLinkedListNode<T> AddBefore(ForkableLinkedListNode<T> node, T value)         {             ValidateNode(node);             ForkableLinkedListNode<T> result = new ForkableLinkedListNode<T>(node.list, value);             InternalInsertNodeBefore(node, result);             if (node == head)             {                 head = result;             }             return result;         }          public void AddBefore(ForkableLinkedListNode<T> node, ForkableLinkedListNode<T> newNode)         {             ValidateNode(node);             ValidateNewNode(newNode);             InternalInsertNodeBefore(node, newNode);             newNode.list = this;             if (node == head)             {                 head = newNode;             }         }          public ForkableLinkedListNode<T> AddFirst(T value)         {             ForkableLinkedListNode<T> result = new ForkableLinkedListNode<T>(this, value);             if (head == null)             {                 InternalInsertNodeToEmptyList(result);             }             else             {                 InternalInsertNodeBefore(head, result);                 head = result;             }             return result;         }          public void AddFirst(ForkableLinkedListNode<T> node)         {             ValidateNewNode(node);              if (head == null)             {                 InternalInsertNodeToEmptyList(node);             }             else             {                 InternalInsertNodeBefore(head, node);                 head = node;             }             node.list = this;         }          public ForkableLinkedListNode<T> AddLast(T value)         {             ForkableLinkedListNode<T> result = new ForkableLinkedListNode<T>(this, value);             if (head == null)             {                 InternalInsertNodeToEmptyList(result);             }             else             {                 InternalInsertNodeBefore(head, result);             }             return result;         }          public void AddLast(ForkableLinkedListNode<T> node)         {             ValidateNewNode(node);              if (head == null)             {                 InternalInsertNodeToEmptyList(node);             }             else             {                 InternalInsertNodeBefore(head, node);             }             node.list = this;         }          public void Clear()         {             ForkableLinkedListNode<T> current = head;             while (current != null)             {                 ForkableLinkedListNode<T> temp = current;                 current = current.Next;   // use Next the instead of "next", otherwise it will loop forever                 temp.Invalidate();             }              head = null;             count = 0;             version++;         }          public bool Contains(T value)         {             return Find(value) != null;         }          public void CopyTo(T[] array, int index)         {             if (array == null)             {                 throw new ArgumentNullException("array");             }              if (index < 0 || index > array.Length)             {                 throw new ArgumentOutOfRangeException("index");             }              if (array.Length - index < Count)             {                 throw new ArgumentException("index");             }              ForkableLinkedListNode<T> node = head;             if (node != null)             {                 do                 {                     array[index++] = node.item;                     node = node.next;                 } while (node != head);             }         }          public ForkableLinkedListNode<T> Find(T value)         {             ForkableLinkedListNode<T> node = head;             EqualityComparer<T> c = EqualityComparer<T>.Default;             if (node != null)             {                 if (value != null)                 {                     do                     {                         if (c.Equals(node.item, value))                         {                             return node;                         }                         node = node.next;                     } while (node != head);                 }                 else                 {                     do                     {                         if (node.item == null)                         {                             return node;                         }                         node = node.next;                     } while (node != head);                 }             }             return null;         }          public ForkableLinkedListNode<T> FindLast(T value)         {             if (head == null) return null;              ForkableLinkedListNode<T> last = head.prev;             ForkableLinkedListNode<T> node = last;             EqualityComparer<T> c = EqualityComparer<T>.Default;             if (node != null)             {                 if (value != null)                 {                     do                     {                         if (c.Equals(node.item, value))                         {                             return node;                         }                          node = node.prev;                     } while (node != last);                 }                 else                 {                     do                     {                         if (node.item == null)                         {                             return node;                         }                         node = node.prev;                     } while (node != last);                 }             }             return null;         }          public IEnumerator GetEnumerator()         {             return new Enumerator(this); // Here I get the aforementioned error.         }          IEnumerator<T> IEnumerable<T>.GetEnumerator()         {             return GetEnumerator();         }          public bool Remove(T value)         {             ForkableLinkedListNode<T> node = Find(value);             if (node != null)             {                 InternalRemoveNode(node);                 return true;             }             return false;         }          public void Remove(ForkableLinkedListNode<T> node)         {             ValidateNode(node);             InternalRemoveNode(node);         }          public void RemoveFirst()         {             if (head == null) { throw new InvalidOperationException(); }             InternalRemoveNode(head);         }          public void RemoveLast()         {             if (head == null) { throw new InvalidOperationException(); }             InternalRemoveNode(head.prev);         }          private void InternalInsertNodeBefore(ForkableLinkedListNode<T> node, ForkableLinkedListNode<T> newNode)         {             newNode.next = node;             newNode.prev = node.prev;             node.prev.next = newNode;             node.prev = newNode;             version++;             count++;         }          private void InternalInsertNodeToEmptyList(ForkableLinkedListNode<T> newNode)         {             Debug.Assert(head == null && count == 0, "LinkedList must be empty when this method is called!");             newNode.next = newNode;             newNode.prev = newNode;             head = newNode;             version++;             count++;         }          internal void InternalRemoveNode(ForkableLinkedListNode<T> node)         {             Debug.Assert(node.list == this, "Deleting the node from another list!");             Debug.Assert(head != null, "This method shouldn't be called on empty list!");             if (node.next == node)             {                 Debug.Assert(count == 1 && head == node, "this should only be true for a list with only one node");                 head = null;             }             else             {                 node.next.prev = node.prev;                 node.prev.next = node.next;                 if (head == node)                 {                     head = node.next;                 }             }             node.Invalidate();             count--;             version++;         }          internal void ValidateNewNode(ForkableLinkedListNode<T> node)         {             if (node == null)             {                 throw new ArgumentNullException("node");             }              if (node.list != null)             {                 throw new InvalidOperationException();             }         }           internal void ValidateNode(ForkableLinkedListNode<T> node)         {             if (node == null)             {                 throw new ArgumentNullException("node");             }              if (node.list != this)             {                 throw new InvalidOperationException();             }         }          bool System.Collections.ICollection.IsSynchronized         {             get { return false; }         }          object System.Collections.ICollection.SyncRoot         {             get             {                 if (_syncRoot == null)                 {                     System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);                 }                 return _syncRoot;             }         }          void System.Collections.ICollection.CopyTo(Array array, int index)         {             if (array == null)             {                 throw new ArgumentNullException("array");             }              if (array.Rank != 1)             {                 throw new ArgumentException("array");             }              if (array.GetLowerBound(0) != 0)             {                 throw new ArgumentException("array");             }              if (index < 0)             {                 throw new ArgumentOutOfRangeException("index");             }              if (array.Length - index < Count)             {                 throw new ArgumentException("index");             }              T[] tArray = array as T[];             if (tArray != null)             {                 CopyTo(tArray, index);             }             else             {                 //                 // Catch the obvious case assignment will fail.                 // We can found all possible problems by doing the check though.                 // For example, if the element type of the Array is derived from T,                 // we can't figure out if we can successfully copy the element beforehand.                 //                 Type targetType = array.GetType().GetElementType();                 Type sourceType = typeof(T);                 if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType)))                 {                     throw new ArgumentException("array");                 }                  object[] objects = array as object[];                 if (objects == null)                 {                     throw new ArgumentException("array");                 }                 ForkableLinkedListNode<T> node = head;                 try                 {                     if (node != null)                     {                         do                         {                             objects[index++] = node.item;                             node = node.next;                         } while (node != head);                     }                 }                 catch (ArrayTypeMismatchException)                 {                     throw new ArgumentException("array");                 }             }         }     } 

And here’s the code for Enumerator, which I copied verbatim and then removed the serialization stuff that I didn’t need. Here, it doesn’t seem to recognize T as a generic type because I don’t use Enumerator<T>. So why does it work in the .NET reference?

public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator // Here, T is not recognized as a generic type     {         private ForkableLinkedList<T> list;         private ForkableLinkedListNode<T> node;         private int version;         private T current;         private int index;         internal Enumerator(ForkableLinkedList<T> list)         {             this.list = list;             version = list.version;             node = list.head;             current = default(T);             index = 0;         }         public T Current         {             get { return current; }         }          object System.Collections.IEnumerator.Current         {             get             {                 if (index == 0 || (index == list.Count + 1))                 {                     throw new InvalidOperationException();                 }                  return current;             }         }          public bool MoveNext()         {             if (version != list.version)             {                 throw new InvalidOperationException();             }              if (node == null)             {                 index = list.Count + 1;                 return false;             }              ++index;             current = node.item;             node = node.next;             if (node == list.head)             {                 node = null;             }             return true;         }          void System.Collections.IEnumerator.Reset()         {             if (version != list.version)             {                 throw new InvalidOperationException();             }              current = default(T);             node = list.head;             index = 0;         }          public void Dispose()         {         }     } 

I’m using .NET Core 3.1.301 in Visual Studio Code 1.46.1 on a Windows 10 (x64) machine.

Add Comment
0 Answer(s)

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.