为 LinkedList 类实现 C# 的 IEnumerable<T> 接口

3
我正在尝试在Linux上使用monoDevelop用C#写一个自定义的LinkedList类,只为了测试和学习。以下代码从来没有编译过,我不知道为什么!它甚至没有告诉我哪里出了问题。它只是说:“错误:编译器似乎已经崩溃。请检查构建输出面板以获取详细信息。”当我去检查输出面板时,它也没有什么帮助: 未处理的异常:System.ArgumentException:指定的字段必须在泛型类型定义中声明。 参数名:字段
我该怎么办?
using System;
using System.Text;
using System.Collections.Generic;

namespace LinkedList
{
    public class myLinkedList<T> : IEnumerable<T>
    {
        //List Node class
        //===============
        private class ListNode<T>
        {
            public T data;
            public ListNode<T> next;

            public ListNode(T d)
            {
                this.data = d;
                this.next = null;
            }

            public ListNode(T d, ListNode<T> n)
            {
                this.data = d;
                this.next = n;
            }
        }

        //priavte fields
        //===============
        private ListNode<T> front;
        private int size;

        //Constructor
        //===========
        public myLinkedList ()
        {
            front = null;
            size = 0;
        }


        //public methods
        //===============
        public bool isEmpty()
        {
            return (size == 0);
        }

        public bool addFront(T element)
        {
            front = new ListNode<T>(element, front);
            size++;
            return true;
        }

        public bool addBack(T element)
        {
            ListNode<T> current = front;
            while (current.next != null)
            {
                current = current.next;
            }

            current.next = new ListNode<T>(element);
            size++;
            return true;
        }

        public override string ToString()
        {
            ListNode<T> current = front;
            if(current == null)
            {
                return "**** Empty ****";
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                while (current.next != null)
                {
                    sb.Append(current.data + ", ");
                    current = current.next;
                }
                sb.Append(current.data);

                return sb.ToString();
            }
        }

        // These make myLinkedList<T> implement IEnumerable<T> allowing
        // a LinkedList to be used in a foreach statement.
        public IEnumerator<T> GetEnumerator()
        {
            return new myLinkedListIterator<T>(front);
        }


        private class myLinkedListIterator<T> : IEnumerator<T>
        {
            private ListNode<T> current;
            public virtual T Current
            {
                get
                {
                    return current.data;
                }
            }
            private ListNode<T> front;

            public myLinkedListIterator(ListNode<T> f)
            {
                front = f;
                current = front;
            }

            public bool MoveNext()
            {
                if(current.next != null)
                {
                    current = current.next;
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public void Reset()
            {
                current = front;
            }

            public void Dispose()
            {
                throw new Exception("Unsupported Operation");
            }
        }
    }
}

2
如果我没记错的话,Dispose 方法会被 foreach 循环调用,因此最好不要在其中抛出异常。 - Magnus
在Dispose()中抛出异常是完全不正确的,但是可以在Reset()中抛出异常;任何标准API都不会使用Reset() - 它基本上已经被弃用了。 - Marc Gravell
哦,顺便说一下 - LinkedList<T> 内部的 Node<T> 不应该是泛型;泛型类型参数是继承的 - 这可以直接使用 Node。编译器会警告你: "类型参数'T'与外部类型 'LinkedList.myLinkedList <T>' 的类型参数名称相同"。 - Marc Gravell
2个回答

15
你需要添加非泛型API,因此请将其添加到迭代器中:
object System.Collections.IEnumerator.Current { get { return Current;  } }

并且可以枚举:

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

然而!如果你手动实现这个功能,你会错过一个技巧。使用"迭代器块(iterator block)"会更加容易。

以下是一个完整的实现;你根本不需要编写枚举器类(可以完全删除myLinkedListIterator<T>):

public IEnumerator<T> GetEnumerator()
{
    var node = front;
    while(node != null)
    {
        yield return node.data;
        node = node.next;
    }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

非常感谢。我尝试了你建议的方法,只有当我将类(myLinkedList、ListNode、myLinkedListIterator)分别放在不同的文件中时才起作用。我猜这些类使用的泛型类型参数<T>之间存在冲突。 - Ahmed Fakhry
@AhmedFakhry,看一下我在帖子上添加的评论;ListNode<T>中的T是错误和多余的。这是一个编译器错误,尽管有多余的T,它应该编译通过。尝试从ListNode<T>中删除T,并将它们保留在同一个文件中。 - Marc Gravell

5
当我尝试运行你复制的代码时,构建时出现了2个错误。
'myLinkedList'未实现接口成员'System.Collections.IEnumerable.GetEnumerator()'. '.myLinkedList.GetEnumerator()'无法实现'System.Collections.IEnumerable.GetEnumerator()',因为它没有与'System.Collections.IEnumerator'相匹配的返回类型。
解决方案是在第一个类中实现以下内容。
IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

第二个错误是:

'myLinkedList.myLinkedListIterator'未实现接口成员'System.Collections.IEnumerator.Current'。 'JonasApplication.myLinkedList.myLinkedListIterator.Current'不能实现'System.Collections.IEnumerator.Current',因为它没有匹配的返回类型'object'。

解决方案可能是在第二个类中实现以下内容:

object IEnumerator.Current { get { return Current; } }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接