C# 集合框架完全指南:从IEnumerable到ObservableCollection的深度解析 - 教程

集合是C#编程的基石,但你真的了解所有集合类型吗?今天带你深入探索C#集合生态系统的每一个角落!

一、集合框架层次结构:全景视图

先来看一下C#集合的完整家族树:

IEnumerable (接口)
├── ICollection
│   ├── IList (有序集合)
│   │   ├── List (动态数组)
│   │   ├── ObservableCollection (可观察集合)
│   │   └── ReadOnlyCollection (只读包装)
│   │
│   ├── ISet (集合运算)
│   │   ├── HashSet (哈希集合)
│   │   └── SortedSet (排序集合)
│   │
│   └── IDictionary
│       ├── Dictionary
│       ├── SortedDictionary
│       └── ReadOnlyDictionary
│
├── IReadOnlyCollection
├── IReadOnlyList
└── IReadOnlyDictionary

二、基础迭代接口: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(元素属性变化)
  // 要监听元素属性变化,需要元素实现INotifyPropertyChanged
  users[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;  // 自动同步更新
    // 添加数据会自动更新UI
    Products.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
│   └── 否 → 需要线程安全吗?
│       ├── 是 → ConcurrentDictionary
│       └── 否 → Dictionary
│
└── 否 → 需要保持插入顺序吗?
    ├── 是 → 需要索引访问吗?
    │   ├── 是 → List
    │   └── 否 → LinkedList 或 Queue/Stack
    │
    └── 否 → 需要去重/集合运算吗?
        ├── 是 → HashSet 或 SortedSet
        └── 否 → 需要延迟执行吗?
            ├── 是 → IEnumerable
            └── 否 → 需要UI绑定吗?
                ├── 是 → ObservableCollection
                └── 否 → List

八、性能最佳实践

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>

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

posted @ 2025-10-12 10:51  wzzkaifa  阅读(19)  评论(0)    收藏  举报