C#中应使用泛型Stack<T>而非非泛型Stack,因后者需装箱拆箱且无类型安全;空栈调用Pop()或Peek()抛InvalidOperationException,须用TryPop/TryPeek避免异常;初始化推荐指定容量防扩容,传数组会逆序压入。

别用非泛型 Stack,直接上 Stack<t></t>;空栈调用 Pop() 或 Peek() 必抛 InvalidOperationException,不是返回 null。
怎么创建和初始化 Stack
必须用泛型构造,类型参数不能省:new Stack<string>()</string>、new Stack<int>(1024)</int>(预设容量防频繁扩容)。传数组初始化时注意:new Stack<int>(new[] { 1, 2, 3 })</int> 得到的是栈顶为 3 的栈,因为元素被逆序压入。若要保持顺序,得写 new Stack<int>(arr.Reverse())</int>。
不推荐用非泛型 Stack(位于 System.Collections):它存 object,要装箱拆箱,没编译期类型检查,已属遗留 API。
Push/Pop/Peek 的行为和陷阱
Push() 安全无条件;但 Pop() 和 Peek() 在空栈上调用会立即抛 InvalidOperationException,不是返回默认值或 null。常见误判是写 if (stack.Count > 0) stack.Pop(),看似安全,但多线程下仍可能因竞态失败。
- 更健壮的写法是用
TryPop(out T result)或TryPeek(out T result)—— 它们返回bool表示是否成功,不抛异常 - 别在
foreach中调用Pop(),会触发“集合被修改”异常 -
Clear()不是原子操作,多线程读写必须加锁,或换用ConcurrentStack<t></t>
遍历和查询栈内容
foreach 遍历按从顶到底顺序(即后进先出顺序),new Stack<int> { 1, 2, 3 }</int> 遍历时输出 3 2 1。如果需要底→顶顺序,得先转数组再反转:stack.ToArray().Reverse() 或 Array.Reverse(stack.ToArray())。
Contains() 是 O(n) 全扫描,不是哈希查找;真要高频查存在性,应搭配 HashSet<t></t> 维护副本,而不是依赖 Stack<t>.Contains()</t>。
Count == 0 是判断空栈最轻量方式;Any() 也可用,但需引入 System.Linq,且底层仍是查 Count。
性能与线程安全关键点
Stack<t></t> 底层是动态数组,Push/Pop 均摊 O(1),但扩容时会复制整块内存。若预估栈深较大(如千级以上),建议显式指定初始容量:new Stack<string>(2048)</string>。
它**不是线程安全的**:SyncRoot 属性仅存在于非泛型版,泛型 Stack<t></t> 根本没有同步机制。多线程场景下,要么手动加锁(如 lock(syncObj)),要么直接改用 ConcurrentStack<t></t> —— 后者所有操作都是线程安全的,且无锁设计性能更好。
最后提醒:别把它当 List 用。没有索引器,不能写 stack[0];也没提供插入中间、查找位置等能力。LIFO 不是它的限制,而是它的契约 —— 用错场景,不如换容器。


















