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
相关产品推荐
相关产品推荐

