C# 集合框架完全指南:从IEnumerable到ObservableCollection的深度解析
C# 集合框架完全指南:从IEnumerable到ObservableCollection的深度解析
- 一、集合框架层次结构:全景视图
- 二、基础迭代接口:IEnumerable<T>
- 核心概念:延迟执行(Deferred Execution)
- 三、只读集合接口家族
- 1. IReadOnlyCollection<T> 和 IReadOnlyList<T>
- 四、可观察集合:ObservableCollection<T>
- WPF/Silverlight数据绑定的核心
- 五、线程安全集合:System.Collections.Concurrent
- 1. ConcurrentBag<T> - 线程安全的无序集合
- 2. ConcurrentDictionary<TKey, TValue> - 线程安全字典
- 3. BlockingCollection<T> - 生产者消费者模式
- 六、特殊用途集合
- 1. SortedSet<T> 和 SortedDictionary<TKey, TValue>
- 2. LinkedList<T> - 双向链表
- 七、集合选择决策树
- 如何选择合适的集合?
- 八、性能最佳实践
- 1. 集合初始化的正确方式
- 2. 避免装箱拆箱
- C# 集合类型全面对比总结表
- 📊 集合类型快速参考表
- 🔄 接口层次结构表
- 🎯 场景选择指南表
- ⚡ 性能对比表
- 🚀 最佳实践速查表
- 初始化优化
- API设计原则
- 线程安全选择
- 📊 C# 集合类型全面对比与实战示例
- 🎯 集合类型特性对比表
- 🔄 Queue〈T〉实战示例:任务队列系统
- 🔄 Stack〈T〉实战示例:撤销重做系统
- 💡 关键差异总结
- Queue〈T〉 vs Stack〈T〉核心区别:
- 选择建议:
- 📝 总结要点
- 总结
集合是C#编程的基石,但你真的了解所有集合类型吗?今天带你深入探索C#集合生态系统的每一个角落!
一、集合框架层次结构:全景视图
先来看一下C#集合的完整家族树:
IEnumerable<T> (接口)
├── ICollection<T>
│ ├── IList<T> (有序集合)
│ │ ├── List<T> (动态数组)
│ │ ├── ObservableCollection<T> (可观察集合)
│ │ └── ReadOnlyCollection<T> (只读包装)
│ │
│ ├── ISet<T> (集合运算)
│ │ ├── HashSet<T> (哈希集合)
│ │ └── SortedSet<T> (排序集合)
│ │
│ └── IDictionary<TKey, TValue>
│ ├── Dictionary<TKey, TValue>
│ ├── SortedDictionary<TKey, TValue>
│ └── ReadOnlyDictionary<TKey, TValue>
│
├── IReadOnlyCollection<T>
├── IReadOnlyList<T>
└── IReadOnlyDictionary<TKey, TValue>
二、基础迭代接口:IEnumerable
核心概念:延迟执行(Deferred Execution)
public class IEnumerableDeepDive
{public static void Demo(){// 数据源List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };// LINQ查询 - 此时不会立即执行IEnumerable<int> evenNumbers = numbers.Where(n => n % 2 == 0);Console.WriteLine("查询已定义,但尚未执行");// 真正执行是在迭代时foreach (var num in evenNumbers) // 此时才执行过滤{Console.WriteLine(num); // 输出: 2, 4}// 修改数据源后再次迭代numbers.Add(6);numbers.Add(8);foreach (var num in evenNumbers) // 重新执行查询!{Console.WriteLine(num); // 输出: 2, 4, 6, 8}}
}// 自定义IEnumerable实现
public class FibonacciSequence : IEnumerable<long>
{public IEnumerator<long> GetEnumerator(){long a = 0, b = 1;while (true){yield return a;long temp = a;a = b;b = temp + b;}}IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}// 使用示例:无限序列(只在需要时计算)
var fibonacci = new FibonacciSequence();
var firstTen = fibonacci.Take(10); // 不会计算全部,只取前10个
foreach (var num in firstTen)
{Console.WriteLine(num); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}
使用场景:
- 需要延迟执行的查询
- 处理大型数据集(内存友好)
- 自定义序列生成器
- LINQ查询的返回类型
三、只读集合接口家族
1. IReadOnlyCollection 和 IReadOnlyList
public class ReadOnlyExamples
{public static void Demo(){List<string> mutableList = new List<string> { "A", "B", "C" };// 转换为只读接口IReadOnlyList<string> readOnlyList = mutableList.AsReadOnly();IReadOnlyCollection<string> readOnlyCollection = mutableList.AsReadOnly();// 可以读取,但不能修改Console.WriteLine(readOnlyList[0]); // ✅ 可以Console.WriteLine(readOnlyList.Count); // ✅ 可以// readOnlyList[0] = "X"; // ❌ 编译错误// readOnlyList.Add("D"); // ❌ 编译错误// 原始集合修改会影响只读视图mutableList.Add("D");Console.WriteLine(readOnlyList.Count); // 输出: 4}
}// API设计最佳实践
public class ProductService
{private List<Product> _products = new List<Product>();// 好的API设计:返回只读接口public IReadOnlyList<Product> GetAllProducts() => _products.AsReadOnly();// 更好的设计:返回IEnumerable(完全封装)public IEnumerable<Product> GetActiveProducts() => _products.Where(p => p.IsActive);// 错误的设计:暴露内部集合public List<Product> GetProductsBad() => _products; // ❌ 危险!
}// 只读字典
public class ConfigurationService
{private readonly Dictionary<string, string> _settings = new Dictionary<string, string>();public IReadOnlyDictionary<string, string> GetSettings(){return new ReadOnlyDictionary<string, string>(_settings);}// 使用索引器提供只读访问public string this[string key] => _settings.TryGetValue(key, out var value) ? value : null;
}
使用场景:
- API设计:防止调用方修改内部数据
- 线程安全:多个线程可以安全读取(但需要同步写操作)
- 封装性:隐藏实现细节,提供稳定接口
四、可观察集合:ObservableCollection
WPF/Silverlight数据绑定的核心
public class ObservableCollectionDemo
{public static void Demo(){var users = new ObservableCollection<User>{new User { Name = "张三", Age = 25 },new User { Name = "李四", Age = 30 }};// 订阅集合变化事件users.CollectionChanged += (sender, e) =>{switch (e.Action){case NotifyCollectionChangedAction.Add:Console.WriteLine($"添加了 {e.NewItems?[0]}");break;case NotifyCollectionChangedAction.Remove:Console.WriteLine($"删除了 {e.OldItems?[0]}");break;case NotifyCollectionChangedAction.Replace:Console.WriteLine($"替换了 {e.OldItems?[0]} 为 {e.NewItems?[0]}");break;case NotifyCollectionChangedAction.Move:Console.WriteLine($"移动了元素");break;case NotifyCollectionChangedAction.Reset:Console.WriteLine($"集合被重置");break;}};// 操作集合会触发事件users.Add(new User { Name = "王五", Age = 28 }); // 触发Add事件users[0].Age = 26; // 不会触发CollectionChanged(元素属性变化)// 要监听元素属性变化,需要元素实现INotifyPropertyChangedusers[0].PropertyChanged += (s, e) => Console.WriteLine($"属性 {e.PropertyName} 发生了变化");}
}public class User : INotifyPropertyChanged
{private string _name;private int _age;public string Name{get => _name;set { _name = value; OnPropertyChanged(); }}public int Age{get => _age;set { _age = value; OnPropertyChanged(); }}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}public override string ToString() => $"{Name} ({Age}岁)";
}// WPF数据绑定实战
public partial class MainWindow : Window
{public ObservableCollection<Product> Products { get; } = new ObservableCollection<Product>();public MainWindow(){InitializeComponent();ProductsListBox.ItemsSource = Products; // 自动同步更新// 添加数据会自动更新UIProducts.Add(new Product { Name = "笔记本电脑", Price = 5999 });Products.Add(new Product { Name = "鼠标", Price = 99 });}private void AddProduct_Click(object sender, RoutedEventArgs e){Products.Add(new Product { Name = "新商品", Price = 100 });// UI会自动更新,无需手动刷新}
}
使用场景:
- WPF、UWP、Xamarin的数据绑定
- 需要实时UI更新的场景
- 监控集合变化的业务逻辑
五、线程安全集合:System.Collections.Concurrent
1. ConcurrentBag - 线程安全的无序集合
public class ConcurrentBagExample
{public static void Demo(){var concurrentBag = new ConcurrentBag<int>();var results = new ConcurrentBag<string>();// 多个生产者并行添加Parallel.For(0, 100, i =>{concurrentBag.Add(i);results.Add($"线程{Task.CurrentId}添加了{i}");});Console.WriteLine($"元素数量: {concurrentBag.Count}");// 并行处理Parallel.ForEach(concurrentBag, item =>{Console.WriteLine($"处理: {item}");});}
}// 实战场景:并行任务结果收集
public class ParallelProcessor
{public async Task<List<Result>> ProcessItemsAsync(List<Input> inputs){var results = new ConcurrentBag<Result>();await Parallel.ForEachAsync(inputs, async (input, cancellationToken) =>{var result = await ProcessItemAsync(input);results.Add(result);});return results.ToList();}
}
2. ConcurrentDictionary<TKey, TValue> - 线程安全字典
public class ConcurrentDictionaryExample
{private static ConcurrentDictionary<string, UserSession> _sessions = new();public static void UpdateUserSession(string userId, string activity){// 原子操作:更新或添加_sessions.AddOrUpdate(userId, // 添加新会话new UserSession { UserId = userId, LastActivity = activity },// 更新现有会话(key, existing) => {existing.LastActivity = activity;existing.AccessCount++;return existing;});}public static UserSession GetUserSession(string userId){return _sessions.TryGetValue(userId, out var session) ? session : null;}
}public class UserSession
{public string UserId { get; set; }public string LastActivity { get; set; }public int AccessCount { get; set; }
}
3. BlockingCollection - 生产者消费者模式
public class ProducerConsumerExample
{private BlockingCollection<WorkItem> _workQueue = new BlockingCollection<WorkItem>(boundedCapacity: 10);public async Task StartProcessing(){// 启动消费者任务var consumerTask = Task.Run(ConsumeWorkItems);// 生产者添加工作项for (int i = 0; i < 100; i++){var workItem = new WorkItem { Id = i, Data = $"工作项{i}" };// 如果队列已满,会阻塞直到有空间_workQueue.Add(workItem);Console.WriteLine($"生产: {workItem.Data}");await Task.Delay(100);}_workQueue.CompleteAdding(); // 通知消费者结束await consumerTask; // 等待消费者完成}private async Task ConsumeWorkItems(){foreach (var workItem in _workQueue.GetConsumingEnumerable()){Console.WriteLine($"消费: {workItem.Data}");await ProcessWorkItem(workItem);}}
}
六、特殊用途集合
1. SortedSet 和 SortedDictionary<TKey, TValue>
public class SortedCollectionsExample
{public static void Demo(){// 自动排序的集合var sortedSet = new SortedSet<int> { 5, 2, 8, 1, 9 };foreach (var num in sortedSet) // 输出: 1, 2, 5, 8, 9{Console.WriteLine(num);}// 排序字典(按Key排序)var sortedDict = new SortedDictionary<string, int>{["张三"] = 90,["李四"] = 85,["王五"] = 92};foreach (var kvp in sortedDict) // 按键名字母顺序排序{Console.WriteLine($"{kvp.Key}: {kvp.Value}");}}
}// 自定义排序规则
public class ProductPriceComparer : IComparer<Product>
{public int Compare(Product x, Product y){return x.Price.CompareTo(y.Price);}
}// 使用自定义比较器
var productsByPrice = new SortedSet<Product>(new ProductPriceComparer());
2. LinkedList - 双向链表
public class LinkedListExample
{public static void Demo(){var linkedList = new LinkedList<string>();// 高效的在头部和尾部添加linkedList.AddFirst("第一个");linkedList.AddLast("最后一个");linkedList.AddAfter(linkedList.First, "中间");// 遍历链表LinkedListNode<string> current = linkedList.First;while (current != null){Console.WriteLine(current.Value);current = current.Next;}// 高效插入删除(不需要移动元素)linkedList.Remove(linkedList.First.Next); // 删除中间节点}
}// 实战场景:LRU缓存实现
public class LRUCache<TKey, TValue> where TKey : notnull
{private readonly int _capacity;private readonly Dictionary<TKey, LinkedListNode<CacheItem>> _cache;private readonly LinkedList<CacheItem> _accessOrder;public LRUCache(int capacity){_capacity = capacity;_cache = new Dictionary<TKey, LinkedListNode<CacheItem>>(capacity);_accessOrder = new LinkedList<CacheItem>();}public TValue Get(TKey key){if (_cache.TryGetValue(key, out var node)){// 移动到头部表示最近使用_accessOrder.Remove(node);_accessOrder.AddFirst(node);return node.Value.Value;}return default;}
}
七、集合选择决策树
如何选择合适的集合?
需要存储键值对吗?
├── 是 → 需要排序吗?
│ ├── 是 → SortedDictionary<TKey, TValue>
│ └── 否 → 需要线程安全吗?
│ ├── 是 → ConcurrentDictionary<TKey, TValue>
│ └── 否 → Dictionary<TKey, TValue>
│
└── 否 → 需要保持插入顺序吗?├── 是 → 需要索引访问吗?│ ├── 是 → List<T>│ └── 否 → LinkedList<T> 或 Queue<T>/Stack<T>│└── 否 → 需要去重/集合运算吗?├── 是 → HashSet<T> 或 SortedSet<T>└── 否 → 需要延迟执行吗?├── 是 → IEnumerable<T>└── 否 → 需要UI绑定吗?├── 是 → ObservableCollection<T>└── 否 → List<T>
八、性能最佳实践
1. 集合初始化的正确方式
// ❌ 不好的做法:多次调整容量
var badList = new List<int>();
for (int i = 0; i < 1000; i++)
{badList.Add(i); // 可能多次扩容
}// ✅ 好的做法:预分配容量
var goodList = new List<int>(1000);
for (int i = 0; i < 1000; i++)
{goodList.Add(i); // 一次分配,无需扩容
}// ✅ 使用集合初始化器
var bestList = new List<int> { 1, 2, 3, 4, 5 };
2. 避免装箱拆箱
// ❌ 不好的做法:使用非泛型集合导致装箱
ArrayList badList = new ArrayList();
badList.Add(1); // 装箱
int value = (int)badList[0]; // 拆箱// ✅ 好的做法:使用泛型集合
List<int> goodList = new List<int>();
goodList.Add(1); // 无装箱
int value = goodList[0]; // 无拆箱
C# 集合类型全面对比总结表
📊 集合类型快速参考表
集合类型 | 命名空间 | 特点 | 时间复杂度 | 线程安全 | 使用场景 | 示例代码 |
---|---|---|---|---|---|---|
List | System.Collections.Generic | 动态数组,有序集合 | 访问: O(1) 添加: O(1)* 插入: O(n) 查找: O(n) | ❌ | 通用集合,需要索引访问 | var list = new List<int> {1, 2, 3}; |
Dictionary<TKey,TValue> | System.Collections.Generic | 键值对哈希表 | 访问: O(1) 添加: O(1) 删除: O(1) | ❌ | 快速按键查找,缓存 | var dict = new Dictionary<string, int>(); |
HashSet | System.Collections.Generic | 不重复元素集合 | 添加: O(1) 查找: O(1) 删除: O(1) | ❌ | 去重操作,集合运算 | var set = new HashSet<int> {1, 2, 2}; |
Queue | System.Collections.Generic | 先进先出(FIFO) | 入队: O(1) 出队: O(1) | ❌ | 任务队列,BFS算法 | var queue = new Queue<string>(); |
Stack | System.Collections.Generic | 后进先出(LIFO) | 压栈: O(1) 弹栈: O(1) | ❌ | 撤销操作,DFS算法 | var stack = new Stack<int>(); |
LinkedList | System.Collections.Generic | 双向链表 | 插入: O(1) 删除: O(1) 访问: O(n) | ❌ | 频繁插入删除 | var list = new LinkedList<int>(); |
ObservableCollection | System.Collections.ObjectModel | 可监听变化的集合 | 同List | ❌ | WPF数据绑定,UI实时更新 | var oc = new ObservableCollection<T>(); |
SortedDictionary<TKey,TValue> | System.Collections.Generic | 按键排序的字典 | 访问: O(log n) 添加: O(log n) | ❌ | 需要有序遍历的键值对 | var sortedDict = new SortedDictionary<int, string>(); |
SortedSet | System.Collections.Generic | 排序的不重复集合 | 添加: O(log n) 查找: O(log n) | ❌ | 需要有序且不重复的集合 | var sortedSet = new SortedSet<int>(); |
ConcurrentDictionary<TKey,TValue> | System.Collections.Concurrent | 线程安全字典 | 访问: O(1) 添加: O(1) | ✅ | 多线程环境下的字典操作 | var concurrentDict = new ConcurrentDictionary<string, int>(); |
ConcurrentBag | System.Collections.Concurrent | 线程安全无序集合 | 添加: O(1) 取出: O(1) | ✅ | 多线程任务结果收集 | var bag = new ConcurrentBag<int>(); |
BlockingCollection | System.Collections.Concurrent | 有界阻塞集合 | 依赖底层集合 | ✅ | 生产者消费者模式 | var bc = new BlockingCollection<T>(); |
ReadOnlyCollection | System.Collections.ObjectModel | 只读集合包装器 | 同底层集合 | ❌ | API返回,防止修改 | var readOnly = list.AsReadOnly(); |
🔄 接口层次结构表
接口 | 描述 | 实现集合 | 特点 |
---|---|---|---|
IEnumerable | 支持迭代 | 所有集合 | 延迟执行,LINQ基础 |
ICollection | 基础集合操作 | List, Dictionary, HashSet | 添加、删除、计数 |
IList | 有序集合 | List, ObservableCollection | 索引访问,插入删除 |
IDictionary<TKey,TValue> | 键值对集合 | Dictionary, SortedDictionary | 按键访问,键值对操作 |
ISet | 集合运算 | HashSet, SortedSet | 并集、交集、差集 |
IReadOnlyCollection | 只读集合 | 所有集合的只读视图 | 防止修改,API设计 |
IReadOnlyList | 只读有序集合 | List的只读视图 | 只读索引访问 |
IReadOnlyDictionary<TKey,TValue> | 只读字典 | Dictionary的只读视图 | 只读键值对访问 |
🎯 场景选择指南表
使用场景 | 推荐集合 | 替代方案 | 理由 |
---|---|---|---|
通用数据存储 | List | Array | 灵活,支持动态扩容 |
快速按键查找 | Dictionary<TKey,TValue> | - | O(1)查找性能 |
数据去重 | HashSet | LINQ Distinct() | 自动去重,集合运算 |
任务队列 | Queue | ConcurrentQueue | FIFO,顺序处理 |
撤销重做 | Stack | - | LIFO,历史记录 |
WPF数据绑定 | ObservableCollection | BindingList | 自动UI更新 |
多线程共享 | ConcurrentDictionary | lock+Dictionary | 内置线程安全 |
生产者消费者 | BlockingCollection | ManualResetEvent | 自动阻塞协调 |
API返回值 | IReadOnlyList | IEnumerable | 防止调用方修改 |
排序数据 | SortedDictionary<TKey,TValue> | Dictionary+LINQ OrderBy | 自动维护顺序 |
大型数据流 | IEnumerable | List | 延迟执行,内存友好 |
⚡ 性能对比表
操作 | List | Dictionary | HashSet | LinkedList | 备注 |
---|---|---|---|---|---|
按索引访问 | ⭐⭐⭐⭐⭐ | ❌ | ❌ | ⭐⭐ | List最优 |
按键/值查找 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | 哈希集合最优 |
头部插入 | ⭐⭐ | ❌ | ❌ | ⭐⭐⭐⭐⭐ | LinkedList最优 |
尾部插入 | ⭐⭐⭐⭐⭐ | ❌ | ❌ | ⭐⭐⭐⭐⭐ | 两者都优 |
中间插入 | ⭐⭐ | ❌ | ❌ | ⭐⭐⭐⭐⭐ | LinkedList最优 |
删除元素 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | List较差 |
内存效率 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | List较好 |
🚀 最佳实践速查表
初始化优化
// ❌ 避免:未知容量时的多次扩容
var list = new List<int>();
for (int i = 0; i < 1000; i++) list.Add(i);// ✅ 推荐:预分配容量
var list = new List<int>(1000);
for (int i = 0; i < 1000; i++) list.Add(i);// ✅ 推荐:使用集合初始化器
var dict = new Dictionary<string, int>
{ ["A"] = 1, ["B"] = 2
};
API设计原则
// ❌ 避免:暴露内部集合
public List<User> GetUsers() => _users;// ✅ 推荐:返回只读接口
public IReadOnlyList<User> GetUsers() => _users.AsReadOnly();// ✅ 更好:返回IEnumerable(完全封装)
public IEnumerable<User> GetActiveUsers() => _users.Where(u => u.IsActive);
线程安全选择
// 单线程环境
var dictionary = new Dictionary<string, int>();// 多线程环境 - 选择1:使用并发集合
var concurrentDict = new ConcurrentDictionary<string, int>();// 多线程环境 - 选择2:手动同步
private readonly object _lock = new object();
lock (_lock)
{dictionary[key] = value;
}
📊 C# 集合类型全面对比与实战示例
下面是一个详细的 C# 集合类型对比表格,包含 Queue 和 Stack 的实战代码示例:
🎯 集合类型特性对比表
集合类型 | 数据结构 | 存取顺序 | 时间复杂度 | 线程安全 | 主要用途 |
---|---|---|---|---|---|
Queue〈T〉 | 队列(FIFO) | 先进先出 | Enqueue: O(1) Dequeue: O(1) Peek: O(1) | ❌ 非线程安全 | 任务调度、消息处理、BFS算法 |
Stack〈T〉 | 栈(LIFO) | 后进先出 | Push: O(1) Pop: O(1) Peek: O(1) | ❌ 非线程安全 | 撤销重做、DFS算法、表达式求值 |
List〈T〉 | 动态数组 | 按索引顺序 | 访问: O(1) 插入: O(n) 查找: O(n) | ❌ 非线程安全 | 通用数据存储、随机访问 |
Dictionary〈K,V〉 | 哈希表 | 无序 | 访问: O(1) 添加: O(1) 删除: O(1) | ❌ 非线程安全 | 键值对存储、快速查找 |
HashSet〈T〉 | 哈希集合 | 无序 | 添加: O(1) 查找: O(1) 删除: O(1) | ❌ 非线程安全 | 去重操作、集合运算 |
🔄 Queue〈T〉实战示例:任务队列系统
using System;
using System.Collections.Generic;/// <summary>
/// 基于 Queue〈T〉的任务队列系统示例
/// 演示先进先出(FIFO)的任务处理机制
/// </summary>
public class TaskQueueSystem
{// 定义任务委托public delegate void TaskAction(string taskName);/// <summary>/// 任务项类,封装任务信息/// </summary>public class TaskItem{public string TaskName { get; set; }public TaskAction Action { get; set; }public DateTime EnqueueTime { get; set; }public TaskItem(string name, TaskAction action){TaskName = name;Action = action;EnqueueTime = DateTime.Now;}}// 任务队列 - 使用 Queue〈T〉存储待处理任务private Queue<TaskItem> _taskQueue = new Queue<TaskItem>();// 锁对象,用于多线程环境下的线程安全private readonly object _queueLock = new object();/// <summary>/// 添加任务到队列尾部/// </summary>/// <param name="taskName">任务名称</param>/// <param name="action">任务执行方法</param>public void EnqueueTask(string taskName, TaskAction action){lock (_queueLock){var task = new TaskItem(taskName, action);_taskQueue.Enqueue(task); // 入队操作Console.WriteLine($"[队列操作] 任务 '{taskName}' 已加入队列,当前队列长度: {_taskQueue.Count}");}}/// <summary>/// 从队列头部取出并执行一个任务/// </summary>public void ProcessNextTask(){TaskItem task = null;lock (_queueLock){if (_taskQueue.Count > 0){task = _taskQueue.Dequeue(); // 出队操作 - 先进先出Console.WriteLine($"[队列操作] 开始处理任务 '{task.TaskName}',队列剩余: {_taskQueue.Count}");}}if (task != null){try{// 执行任务task.Action(task.TaskName);Console.WriteLine($"[任务完成] '{task.TaskName}' 执行成功");}catch (Exception ex){Console.WriteLine($"[任务失败] '{task.TaskName}' 执行异常: {ex.Message}");}}else{Console.WriteLine("[队列状态] 任务队列为空,无任务可处理");}}/// <summary>/// 查看队列头部的任务但不移除(Peek操作)/// </summary>public void PeekNextTask(){lock (_queueLock){if (_taskQueue.Count > 0){var nextTask = _taskQueue.Peek(); // 查看队首元素Console.WriteLine($"[队列查看] 下一个任务: '{nextTask.TaskName}',入队时间: {nextTask.EnqueueTime}");}else{Console.WriteLine("[队列查看] 队列为空");}}}/// <summary>/// 获取队列当前状态/// </summary>public void DisplayQueueStatus(){lock (_queueLock){Console.WriteLine($"[队列状态] 当前任务数量: {_taskQueue.Count}");if (_taskQueue.Count > 0){Console.WriteLine("队列中的任务:");int position = 1;foreach (var task in _taskQueue){Console.WriteLine($" {position}. {task.TaskName} (入队时间: {task.EnqueueTime:HH:mm:ss})");position++;}}}}
}// 使用示例
class QueueExample
{static void Main(){var taskSystem = new TaskQueueSystem();// 定义几个示例任务TaskQueueSystem.TaskAction simpleTask = (name) => {Console.WriteLine($" 正在执行: {name}");System.Threading.Thread.Sleep(1000); // 模拟任务执行时间};// 添加任务到队列(Enqueue操作)taskSystem.EnqueueTask("数据备份任务", simpleTask);taskSystem.EnqueueTask("日志清理任务", simpleTask);taskSystem.EnqueueTask("系统检查任务", simpleTask);// 查看队列状态taskSystem.DisplayQueueStatus();// 查看下一个任务但不移除taskSystem.PeekNextTask();// 按顺序处理所有任务(Dequeue操作)Console.WriteLine("\n开始处理队列中的任务:");while (true){taskSystem.ProcessNextTask();if (new System.Random().Next(0, 3) == 0) // 模拟随机添加新任务{taskSystem.EnqueueTask($"随机添加的任务-{DateTime.Now.Second}", simpleTask);}if (taskSystem.GetType().GetField("_taskQueue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(taskSystem) is Queue<TaskQueueSystem.TaskItem> queue && queue.Count == 0){break;}System.Threading.Thread.Sleep(500);}}
}
🔄 Stack〈T〉实战示例:撤销重做系统
using System;
using System.Collections.Generic;
using System.Text;/// <summary>
/// 基于 Stack〈T〉的撤销重做系统示例
/// 演示后进先出(LIFO)的操作历史管理
/// </summary>
public class UndoRedoSystem
{/// <summary>/// 文本操作命令接口/// </summary>public interface ITextCommand{string Execute(string currentText);string Undo(string currentText);string Description { get; }}/// <summary>/// 添加文本命令/// </summary>public class AddTextCommand : ITextCommand{public string TextToAdd { get; }public string Description => $"添加文本: '{TextToAdd}'";public AddTextCommand(string text){TextToAdd = text;}public string Execute(string currentText){return currentText + TextToAdd;}public string Undo(string currentText){if (currentText.EndsWith(TextToAdd)){return currentText.Substring(0, currentText.Length - TextToAdd.Length);}return currentText;}}/// <summary>/// 删除文本命令/// </summary>public class DeleteCommand : ITextCommand{public int Count { get; }private string _deletedText;public string Description => $"删除 {Count} 个字符";public DeleteCommand(int count){Count = count;}public string Execute(string currentText){if (currentText.Length >= Count){_deletedText = currentText.Substring(currentText.Length - Count);return currentText.Substring(0, currentText.Length - Count);}_deletedText = currentText;return string.Empty;}public string Undo(string currentText){return currentText + _deletedText;}}// 撤销栈 - 存储已执行的操作(后进先出)private Stack<ITextCommand> _undoStack = new Stack<ITextCommand>();// 重做栈 - 存储已撤销的操作private Stack<ITextCommand> _redoStack = new Stack<ITextCommand>();private string _currentText = string.Empty;/// <summary>/// 执行新命令/// </summary>public void ExecuteCommand(ITextCommand command){// 执行命令_currentText = command.Execute(_currentText);// 将命令压入撤销栈(Push操作)_undoStack.Push(command);// 清空重做栈(执行新命令后重做历史失效)_redoStack.Clear();Console.WriteLine($"[命令执行] {command.Description}");Console.WriteLine($"[当前文本] '{_currentText}'");DisplayStacksStatus();}/// <summary>/// 撤销上一次操作/// </summary>public void Undo(){if (_undoStack.Count > 0){// 从撤销栈弹出最后一个命令(Pop操作)ITextCommand command = _undoStack.Pop();// 执行撤销操作_currentText = command.Undo(_currentText);// 将命令压入重做栈_redoStack.Push(command);Console.WriteLine($"[撤销操作] 撤销: {command.Description}");Console.WriteLine($"[当前文本] '{_currentText}'");DisplayStacksStatus();}else{Console.WriteLine("[撤销操作] 无可撤销的操作");}}/// <summary>/// 重做上一次撤销的操作/// </summary>public void Redo(){if (_redoStack.Count > 0){// 从重做栈弹出命令ITextCommand command = _redoStack.Pop();// 重新执行命令_currentText = command.Execute(_currentText);// 将命令压回撤销栈_undoStack.Push(command);Console.WriteLine($"[重做操作] 重做: {command.Description}");Console.WriteLine($"[当前文本] '{_currentText}'");DisplayStacksStatus();}else{Console.WriteLine("[重做操作] 无可重做的操作");}}/// <summary>/// 查看撤销栈顶部的命令但不移除(Peek操作)/// </summary>public void PeekUndoStack(){if (_undoStack.Count > 0){var nextUndo = _undoStack.Peek();Console.WriteLine($"[栈查看] 下一个可撤销的操作: {nextUndo.Description}");}else{Console.WriteLine("[栈查看] 撤销栈为空");}}/// <summary>/// 显示栈状态/// </summary>private void DisplayStacksStatus(){Console.WriteLine($"[栈状态] 撤销栈: {_undoStack.Count} 个操作, 重做栈: {_redoStack.Count} 个操作");}/// <summary>/// 获取当前文本内容/// </summary>public string GetCurrentText() => _currentText;
}// 使用示例
class StackExample
{static void Main(){var undoSystem = new UndoRedoSystem();// 执行一系列操作Console.WriteLine("=== 执行操作序列 ===");undoSystem.ExecuteCommand(new UndoRedoSystem.AddTextCommand("Hello"));undoSystem.ExecuteCommand(new UndoRedoSystem.AddTextCommand(" World"));undoSystem.ExecuteCommand(new UndoRedoSystem.AddTextCommand("!"));undoSystem.ExecuteCommand(new UndoRedoSystem.DeleteCommand(6)); // 删除" World"Console.WriteLine("\n=== 撤销操作 ===");// 撤销操作(LIFO顺序)undoSystem.Undo(); // 撤销删除undoSystem.Undo(); // 撤销添加"!"Console.WriteLine("\n=== 重做操作 ===");// 重做操作undoSystem.Redo(); // 重做添加"!"undoSystem.Redo(); // 重做删除(但只有一个可重做)Console.WriteLine("\n=== 查看栈状态 ===");undoSystem.PeekUndoStack();Console.WriteLine($"最终文本: '{undoSystem.GetCurrentText()}'");}
}
💡 关键差异总结
Queue〈T〉 vs Stack〈T〉核心区别:
特性 | Queue〈T〉(队列) | Stack〈T〉(栈) |
---|---|---|
存取原则 | 先进先出(FIFO) | 后进先出(LIFO) |
典型操作 | Enqueue(入队)、Dequeue(出队) | Push(压栈)、Pop(弹栈) |
应用场景 | 任务调度、消息处理、BFS算法 | 撤销重做、DFS算法、括号匹配 |
数据结构 | 线性结构,两端开放 | 线性结构,仅一端开放 |
线程安全 | 需要手动同步或使用ConcurrentQueue | 需要手动同步或使用ConcurrentStack |
选择建议:
- 需要顺序处理(如任务队列、消息处理) → 选择 Queue〈T〉
- 需要反向操作(如撤销功能、路径回溯) → 选择 Stack〈T〉
- 多线程环境 → 考虑 ConcurrentQueue〈T〉 或 ConcurrentStack〈T〉
这两个数据结构在各自的适用场景下性能优异,理解它们的特性可以帮助你写出更高效的代码。
📝 总结要点
- List是万金油 - 大部分场景的首选
- Dictionary用于快速查找 - 按键访问的最佳选择
- HashSet用于去重 - 集合运算的利器
- 并发集合用于多线程 - 简化线程同步复杂度
- 只读接口用于API设计 - 提高代码健壮性
- IEnumerable用于大数据集 - 延迟执行节省内存
这个表格可以作为日常开发的快速参考指南,帮助你根据具体需求选择最合适的集合类型!
总结
C#集合框架提供了丰富的选择,关键是理解每种集合的特点和适用场景:
- 日常开发:
List<T>
、Dictionary<TKey, TValue>
- 数据绑定:
ObservableCollection<T>
- 只读API:
IReadOnlyCollection<T>
、IReadOnlyList<T>
- 多线程:
ConcurrentDictionary<TKey, TValue>
、BlockingCollection<T>
- 特殊需求:
SortedSet<T>
、LinkedList<T>
、HashSet<T>
记住:选择集合就是选择数据结构,选择数据结构就是选择算法。正确的集合选择能让你的代码性能提升一个数量级!