c# - 使用 IEnumerable<T> 和 Array 制作的自定义 Stack<T>?

标签 c# arrays stack ienumerable

我一直在努力解决这个问题。 我试图让 CustomStack 像 Stack 一样工作,只实现 Push(T)、Pop()、Peek() 和 Clear() 方法。我有这段代码,我认为它是正确的,但输出只显示了一半的数字。我认为这与 push 方法有关,但我看不出它有什么问题。

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];

                }
            }
        }
    }
}

最佳答案

您正在做一些您被要求永远不会的事情:您正在修改一个集合,同时用一个枚举器迭代它。 (foreach 循环是用于分配枚举器的语法糖。)

IEnumerable 的文档实际上建议如果您的数据结构在枚举时被修改,像您这样的实现抛出异常。 (用 List<T> 试试,你会看到;如果在 foreach 中枚举列表时添加或删除项目,列表将抛出。)

这就是你的问题的原因;您的数据结构并非旨在 (1) 在被滥用时抛出,或 (2) 在被滥用时表现良好,因此当您滥用它时它表现得很糟糕。

我的建议:如果这样做会造成伤害,请不要这样做。不要在枚举集合的循环中修改集合。

相反,制作一个 IsEmpty属性并编写你的循环:

while(!collection.IsEmpty)  
  Console.WriteLine(collection.Pop());

这样您就不会修改集合同时有一个枚举器正在处理

您在这里遇到的具体问题是:position每次通过循环总是增加。和 count总是在减少。你说只有一半的项目被计算在内。好吧,解决它。如果你有 10 个项目,position 从 0 开始,递增直到大于 count,那么每次循环...

position    count
 0           10
 1           9
 2           8
 3           7
 4           6
 5           5  

我们已经完成了,我们只列举了一半的项目。

如果你想让你的集合在迭代时面对被修改的健壮性,那么 position当堆栈被压入或弹出时必须改变。即使计数在变化,它也不能每次都盲目增加。找出正确的行为非常棘手,这就是文档建议您直接抛出的原因。

如果你想让你的集合在被枚举时修改时抛出异常,技巧是让对象有一个称为“版本号”的整数。每次推送或弹出集合时,更改版本号。然后让迭代器在迭代开始时获取版本号的副本;如果它检测到当前版本号与副本不同,则集合在枚举期间已被修改,您可以抛出集合修改异常。

感谢您提出有趣的问题;我可能会在我的博客中将它用作示例,并且可能会看看我是否可以编写一个静态分析器来检测这种危险的修改。

关于c# - 使用 IEnumerable<T> 和 Array 制作的自定义 Stack<T>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16494395/

相关文章:

c - 为什么函数堆栈帧中的参数、变量和帧指针之间存在间隙?

java - 堆栈上的堆分配(使用堆栈作为堆管理系统)

c# - 什么是最先进的自动更新技术?

c# - WPF 数据绑定(bind) : How can I bind a stored procedure to a Listview

arrays - 如何将 "merge"两个数组放在一起? (逐项连接字符串)

java - 使用堆栈理解 Java 代码中的后缀表达式求值

c# - 如何在 C# 中减去两个通用对象 (T - T)(示例 : DateTime - DateTime)?

c# - Xamarin 自定义 UITableViewCell 抛出系统 NullReferenceException

c - 请帮助解释这个域正在做什么(使用函数在数组中添加斐波那契序列元素的程序)

java - 程序编译,但运行时数组越界?