use*_*601 1 c# arrays ienumerable stack
我有这个问题,我一直想弄明白.我试图使CustomStack像Stack一样,只实现Push(T),Pop(),Peek()和Clear()方法.我有这个代码,我认为它是正确的,但输出只显示一半的数字.我认为这与推送方法有关,但我看不出它有什么问题.
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
namespace Enumerator
{
    class Program
    {
        static void Main(string[] args)
        {
            CustomStack<int> collection = new CustomStack<int>();
            for (int i = 0; i < 30; i++)
            {
                collection.Push(i);
                Console.WriteLine(collection.Peek());
            }
            collection.Push(23);
            foreach (int x in collection)
            {
                Console.WriteLine(collection.Pop());
            }
            Console.WriteLine("current", collection.Peek());
            Console.ReadKey();
        }
    }
    public class CustomStack<T> : IEnumerable<T>
    {
        private T[] arr;
        private int count;
        public CustomStack()
        {
            count = 0;
            arr = new T[5];
        }
        public T Pop()
        {
            int popIndex = count;
            if (count > 0)
            {
                count--;
                return arr[popIndex];
            }
            else
            {
                return arr[count];
            }
        }
        public void Push(T item)
        {
            count++;
            if (count == arr.Length)
            {
                Array.Resize(ref arr, arr.Length + 1);
            }
            arr[count] = item;
        }
        public void Clear()
        {
            count = 0;
        }
        public T Peek()
        {
            return arr[count];
        }
        public int Count
        {
            get
            {
                return count;
            }
        }
        public IEnumerator<T> GetEnumerator()
        {
            return new MyEnumerator(this);
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new MyEnumerator(this);
        }
        public class MyEnumerator : IEnumerator<T>
        {
            private int position;
            private CustomStack<T> stack;
            public MyEnumerator(CustomStack<T> stack)
            {
                this.stack = stack;
                position = -1;
            }
            public void Dispose()
            {
            }
            public void Reset()
            {
                position = -1;
            }
            public bool MoveNext()
            {
                position++;
                return position < stack.Count;
            }
            Object IEnumerator.Current
            {
                get
                {
                    return stack.arr[position];
                }
            }
            public T Current
            {
                get
                {
                    return stack.arr[position];
                }
            }
        }
    }
}
Eri*_*ert 15
您正在做一些您永远不会做的事情:您在使用枚举器迭代时修改集合.(循环是用于分配枚举器的语法糖.)  foreach
IEnumerable实际的文档表明,如果在枚举时修改了数据结构,那么像您这样的实现会抛出异常.(尝试一下,List<T>你会看到;如果你在枚举列表时添加或删除一个项目,列表将抛出foreach.)
这是你问题的原因; 你的数据结构不是为了(1)在被滥用时抛弃,或者(2)在被滥用时表现良好,因此当你滥用它时它表现得很糟糕.
我的建议是:如果你这样做会伤害,不要这样做.在枚举它的循环中不要修改集合.
相反,创建一个IsEmpty属性并编写循环:
while(!collection.IsEmpty)  
  Console.WriteLine(collection.Pop());
这样,在同时处理枚举器时,您不会修改集合.
你在这里遇到的具体问题是:position每次循环都会增加.并且count总是在减少.你说只有一半的物品被计算在内.好吧,搞定吧.如果你有10个项目,则位置从零开始,并且增加直到它大于计数,然后每次通过循环......
position    count
 0           10
 1           9
 2           8
 3           7
 4           6
 5           5  
我们已经完成了,我们只列举了一半的项目.
如果你想在迭代position时修改你的集合是健壮的,那么在推送或弹出堆栈时必须改变.即使计数在变化,也不能盲目地增加每次.制定正确的行为非常棘手,这就是为什么文档建议你简单地抛出.
如果你想让你的集合在枚举时修改异常,那么诀窍就是让对象有一个名为"版本号"的int.每次推送或弹出集合时,请更改版本号.然后让迭代器在迭代开始时获取版本号的副本; 如果它检测到当前版本号与副本不同,则在枚举期间修改了集合,您可以抛出集合修改的异常.
感谢有趣的问题; 我可能会在我的博客中使用它作为一个例子,并且可能看看我是否可以编写一个静态分析器来检测这种危险的修改.