You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#自定义泛型Stack类的实现困惑与求助

泛型Stack类的完整实现

你原代码的核心问题是错误依赖了非泛型Stack类,违背了自主实现泛型栈的要求。我们需要用泛型数组作为底层存储来实现所有功能,以下是完整实现及关键逻辑说明:

完整实现代码

using System;
using System.Collections;
using System.Collections.Generic;

namespace GenericStackTask
{
    /// <summary>
    /// 表示指定类型T的栈
    /// </summary>
    /// <typeparam name="T">指定栈中元素的类型</typeparam>
    public class Stack<T> : IEnumerable<T>
    {
        private T[] _items;
        private int _count;
        private const int DefaultCapacity = 4;

        /// <summary>
        /// 初始化一个空栈实例,使用默认初始容量
        /// </summary>
        public Stack()
        {
            _items = new T[DefaultCapacity];
            _count = 0;
        }

        /// <summary>
        /// 初始化一个空栈实例,使用指定的初始容量
        /// </summary>
        /// <param name="capacity">栈的初始元素容量</param>
        /// <exception cref="ArgumentOutOfRangeException">当容量为负数时抛出</exception>
        public Stack(int capacity)
        {
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity), "容量不能为负数");
            
            _items = new T[capacity];
            _count = 0;
        }

        /// <summary>
        /// 初始化一个包含指定集合元素的栈实例,容量足以容纳所有复制的元素
        /// </summary>
        /// <param name="collection">要复制元素的集合</param>
        /// <exception cref="ArgumentNullException">当集合为null时抛出</exception>
        public Stack(IEnumerable<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException(nameof(collection));
            
            var array = new List<T>(collection).ToArray();
            _items = new T[array.Length * 2]; // 预留一倍空间避免频繁扩容
            _count = array.Length;
            Array.Copy(array, _items, array.Length);
        }

        /// <summary>
        /// 获取栈中包含的元素数量
        /// </summary>
        public int Count => _count;

        /// <summary>
        /// 移除并返回栈顶的对象
        /// </summary>
        /// <returns>从栈顶移除的对象</returns>
        /// <exception cref="InvalidOperationException">当栈为空时抛出</exception>
        public T Pop()
        {
            if (_count == 0)
                throw new InvalidOperationException("栈为空");
            
            _count--;
            var item = _items[_count];
            // 清空引用类型的引用,辅助GC回收
            if (typeof(T).IsClass)
                _items[_count] = default;
            
            return item;
        }

        /// <summary>
        /// 返回栈顶的对象但不移除它
        /// </summary>
        /// <returns>栈顶的对象</returns>
        /// <exception cref="InvalidOperationException">当栈为空时抛出</exception>
        public T Peek()
        {
            if (_count == 0)
                throw new InvalidOperationException("栈为空");
            
            return _items[_count - 1];
        }

        /// <summary>
        /// 将对象插入栈顶
        /// </summary>
        /// <param name="item">要推入栈的对象,引用类型可以为null</param>
        public void Push(T item)
        {
            // 容量不足时扩容为当前的2倍
            if (_count == _items.Length)
                Array.Resize(ref _items, _items.Length * 2);
            
            _items[_count] = item;
            _count++;
        }

        /// <summary>
        /// 将栈的元素复制到新数组中
        /// </summary>
        /// <returns>包含栈元素副本的新数组</returns>
        public T[] ToArray()
        {
            var result = new T[_count];
            // 栈是后进先出,数组需倒序复制以匹配弹出顺序
            for (int i = 0; i < _count; i++)
            {
                result[i] = _items[_count - 1 - i];
            }
            return result;
        }

        /// <summary>
        /// 确定元素是否在栈中
        /// </summary>
        /// <param name="item">要在栈中查找的对象,引用类型可以为null</param>
        /// <returns>如果在栈中找到item则返回true,否则返回false</returns>
        public bool Contains(T item)
        {
            var comparer = EqualityComparer<T>.Default;
            for (int i = 0; i < _count; i++)
            {
                if (comparer.Equals(_items[i], item))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 移除栈中的所有对象
        /// </summary>
        public void Clear()
        {
            // 清空引用类型的元素引用
            if (typeof(T).IsClass)
            {
                for (int i = 0; i < _count; i++)
                {
                    _items[i] = default;
                }
            }
            _count = 0;
        }

        /// <summary>
        /// 返回栈的枚举器
        /// </summary>
        /// <returns>栈的枚举器对象</returns>
        public IEnumerator<T> GetEnumerator()
        {
            // 从栈顶到栈底遍历
            for (int i = _count - 1; i >= 0; i--)
            {
                yield return _items[i];
            }
        }

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

关键逻辑说明

1. 底层存储设计

使用泛型数组T[] _items作为存储容器,保证类型安全且避免装箱拆箱开销。用_count记录当前元素数量,_items.Length表示当前栈的容量。

2. 构造函数实现

  • 无参构造:初始化默认容量(4)的数组,元素计数设为0。
  • 指定容量构造:先校验容量合法性,再初始化对应大小的数组。
  • 集合初始化构造:将集合转为数组后,预留一倍容量减少后续扩容频率,最后复制元素到内部数组。

3. 核心方法实现

  • Push:检查元素数量是否等于容量,满额时将数组扩容为原大小的2倍,再将元素放入数组末尾并增加计数。
  • Pop:先判断栈是否为空,计数自减后取出对应元素,对引用类型清空原位置引用辅助GC,最后返回元素。
  • Peek:直接返回栈顶元素(_items[_count-1]),同样需要先检查栈是否为空。

4. 辅助方法实现

  • ToArray:创建新数组并倒序复制内部元素,保证数组顺序与栈的弹出顺序一致(栈顶元素在数组首位)。
  • Contains:使用EqualityComparer<T>.Default处理元素比较,兼容值类型和可空引用类型的比较逻辑。
  • Clear:清空引用类型的元素引用后重置计数,无需重建数组以节省内存开销。

5. 枚举器实现

通过yield return从栈顶到栈底遍历元素,符合栈的后进先出遍历逻辑,同时实现非泛型GetEnumerator方法以兼容旧集合接口。

内容的提问来源于stack exchange,提问作者Akzhol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 11:03:19