Queue初始化必须指定泛型类型,否则创建的是非泛型版本,导致装箱、无智能提示等问题;它非线程安全,多线程需加锁或改用ConcurrentQueue;Peek前须判空或用TryPeek;Clear仅逻辑清空,不立即释放内存。

Queue 初始化时别直接 new Queue() 而不指定泛型类型
这是新手最常踩的坑:写 new Queue() 看似能编译,但实际创建的是非泛型 Queue(来自 System.Collections),不是你想要的 Queue(来自 System.Collections.Generic)。后者才支持强类型、无装箱、IDE 智能提示和 LINQ 扩展。
正确做法永远是显式声明泛型参数:
var q = new Queue(); Queueq = new(); // C# 9+ 目标类型推导也行 - 如果从集合初始化:
var q = new Queue(new[] { "a", "b" });
混用非泛型 Queue 会导致运行时 InvalidCastException 或意外的 object 转换,尤其在 foreach 中取值时。
Enqueue 和 Dequeue 的线程安全边界必须自己控制
Queue 本身不是线程安全的。多线程下同时调用 Enqueue 和 Dequeue 可能引发 InvalidOperationException(“Collection was modified”)或返回错误元素。
不要依赖文档里那句“仅读操作是线程安全的”来冒险——因为 Dequeue 是修改操作,Count 和 Peek 虽然只读,但在并发场景下读到的已是过期状态。
- 简单场景:用
lock包裹关键段,锁对象建议用私有 readonly 字段,别锁this或q(可能被外部锁定) - 高并发场景:改用
ConcurrentQueue,它提供TryDequeue和TryEnqueue这类无异常、返回 bool 的原子方法 - 千万别在 foreach 中边遍历边
Dequeue——这会直接抛出InvalidOperationException
Peek 不取走元素,但不代表“绝对安全”
Peek 确实只看队首不移除,适合做预判。但它有个隐性前提:队列非空。否则抛 InvalidOperationException:“Operation is not valid due to the current state of the object”。
这不是设计缺陷,而是 API 明确契约——它不负责空检查,就像数组索引器不检查越界一样。
- 安全写法总是先判断:
if (q.Count > 0) { var top = q.Peek(); ... } - 或者用
TryPeek(.NET 6+):if (q.TryPeek(out var item)) { ... },避免异常开销 - 注意:即使
TryPeek成功,也不能保证紧接着Dequeue一定拿到同一元素——中间可能被其他线程取走(除非加锁或用ConcurrentQueue)
Clear 后的内存行为容易被误读
Clear() 会清空所有元素并把内部数组长度重置为 0(实际是设为默认初始容量,通常是 4),但不会立即释放底层数组内存——它只是把所有引用设为 null(对引用类型)或清零(对值类型),数组对象仍存活。
这意味着:
- 如果队列曾存过大量大对象(如
byte[]),Clear()后这些对象若无其他引用,会被 GC 回收;但队列自己的内部数组对象不会马上消失 - 频繁 Clear + 大量 Enqueue 可能导致内部数组反复扩容缩容,影响性能
- 真要彻底释放资源?没有公开 API。要么新建实例:
q = new Queue,要么用(); TrimExcess()(仅 .NET 5+)强制收缩到当前元素数所需最小容量
别指望 Clear() 像 GC.Collect 那样“立刻腾内存”,它只是逻辑清空。










