当前位置: 首页 > news >正文

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# 集合类型全面对比总结表

📊 集合类型快速参考表

集合类型命名空间特点时间复杂度线程安全使用场景示例代码
ListSystem.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>();
HashSetSystem.Collections.Generic不重复元素集合添加: O(1)
查找: O(1)
删除: O(1)
去重操作,集合运算var set = new HashSet<int> {1, 2, 2};
QueueSystem.Collections.Generic先进先出(FIFO)入队: O(1)
出队: O(1)
任务队列,BFS算法var queue = new Queue<string>();
StackSystem.Collections.Generic后进先出(LIFO)压栈: O(1)
弹栈: O(1)
撤销操作,DFS算法var stack = new Stack<int>();
LinkedListSystem.Collections.Generic双向链表插入: O(1)
删除: O(1)
访问: O(n)
频繁插入删除var list = new LinkedList<int>();
ObservableCollectionSystem.Collections.ObjectModel可监听变化的集合同ListWPF数据绑定,UI实时更新var oc = new ObservableCollection<T>();
SortedDictionary<TKey,TValue>System.Collections.Generic按键排序的字典访问: O(log n)
添加: O(log n)
需要有序遍历的键值对var sortedDict = new SortedDictionary<int, string>();
SortedSetSystem.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>();
ConcurrentBagSystem.Collections.Concurrent线程安全无序集合添加: O(1)
取出: O(1)
多线程任务结果收集var bag = new ConcurrentBag<int>();
BlockingCollectionSystem.Collections.Concurrent有界阻塞集合依赖底层集合生产者消费者模式var bc = new BlockingCollection<T>();
ReadOnlyCollectionSystem.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的只读视图只读键值对访问

🎯 场景选择指南表

使用场景推荐集合替代方案理由
通用数据存储ListArray灵活,支持动态扩容
快速按键查找Dictionary<TKey,TValue>-O(1)查找性能
数据去重HashSetLINQ Distinct()自动去重,集合运算
任务队列QueueConcurrentQueueFIFO,顺序处理
撤销重做Stack-LIFO,历史记录
WPF数据绑定ObservableCollectionBindingList自动UI更新
多线程共享ConcurrentDictionarylock+Dictionary内置线程安全
生产者消费者BlockingCollectionManualResetEvent自动阻塞协调
API返回值IReadOnlyListIEnumerable防止调用方修改
排序数据SortedDictionary<TKey,TValue>Dictionary+LINQ OrderBy自动维护顺序
大型数据流IEnumerableList延迟执行,内存友好

⚡ 性能对比表

操作ListDictionaryHashSetLinkedList备注
按索引访问⭐⭐⭐⭐⭐⭐⭐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〉

这两个数据结构在各自的适用场景下性能优异,理解它们的特性可以帮助你写出更高效的代码。

📝 总结要点

  1. List是万金油 - 大部分场景的首选
  2. Dictionary用于快速查找 - 按键访问的最佳选择
  3. HashSet用于去重 - 集合运算的利器
  4. 并发集合用于多线程 - 简化线程同步复杂度
  5. 只读接口用于API设计 - 提高代码健壮性
  6. IEnumerable用于大数据集 - 延迟执行节省内存

这个表格可以作为日常开发的快速参考指南,帮助你根据具体需求选择最合适的集合类型!

总结

C#集合框架提供了丰富的选择,关键是理解每种集合的特点和适用场景:

  1. 日常开发List<T>Dictionary<TKey, TValue>
  2. 数据绑定ObservableCollection<T>
  3. 只读APIIReadOnlyCollection<T>IReadOnlyList<T>
  4. 多线程ConcurrentDictionary<TKey, TValue>BlockingCollection<T>
  5. 特殊需求SortedSet<T>LinkedList<T>HashSet<T>

记住:选择集合就是选择数据结构,选择数据结构就是选择算法。正确的集合选择能让你的代码性能提升一个数量级!

http://www.dtcms.com/a/410729.html

相关文章:

  • 用户研究:用户研究和数据分析的根本联系与区别
  • 网站关键词优化培训jeecg 3.7 网站开发
  • 右键菜单增强工具,自定义系统功能
  • 图像分类入门:从数据到模型的深度学习核心知识解析
  • 攻防世界-Web-PHP2
  • Windows系统Web UI自动化测试学习系列3--浏览器驱动下载使用
  • 00-为什么要系统学习正则表达式?
  • 湖北网站建设检修金融股票类app网站开发
  • C++ 序列容器深度解析:vector、deque 与 list
  • 提供企业网站建设上海公司注册一网通办
  • 高效的技术支持提升用户体验
  • 满山红网站建设做家装的网站有什么
  • 建设部网站社保联网小程序注册平台
  • Mysql中GROUP_CONCAT分组聚合函数的使用以及示例
  • 2025无人机林业行业场景解决方案
  • 化肥网站模板青岛建设集团 招聘信息网站
  • 【在Ubuntu 24.04.2 LTS上安装Qt 6.9.2】
  • 家居企业网站建设渠道百度如何推广广告
  • 《MLB美职棒》运动员体质特征·棒球1号位
  • AI 应用和工业软件
  • 网站备案空壳网站制作找
  • 洛谷 P3388:【模板】割点(割顶)← Tarjan 算法
  • DeepSeek“问道”-第二章:问算法 —— 阴与阳如何在我内部舞蹈?
  • 重学JS-009 --- JavaScript算法与数据结构(九)Javascript 方法
  • Python项目中ModuleNotFoundError与FileNotFoundError的深度解决指南(附实战案例)
  • LeetCode:61.分割回文串
  • 坑: console.log,对象引用机制
  • 网站模板找超速云建站学校网站建设是什么意思
  • 做购物网站的业务微信公众号开发网站开发
  • Matlab通过GUI实现点云的均值滤波(附最简版)