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

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

  • [C# 集合类型全面对比总结表](# 集合类型全面对比总结表)
    • [📊 集合类型快速参考表](#📊 集合类型快速参考表)
    • [🔄 接口层次结构表](#🔄 接口层次结构表)
    • [🎯 场景选择指南表](#🎯 场景选择指南表)
    • [⚡ 性能对比表](#⚡ 性能对比表)
    • [🚀 最佳实践速查表](#🚀 最佳实践速查表)
    • [📊 C# 集合类型全面对比与实战示例](# 集合类型全面对比与实战示例)
      • [🎯 集合类型特性对比表](#🎯 集合类型特性对比表)
    • [🔄 Queue〈T〉实战示例:任务队列系统](#🔄 Queue〈T〉实战示例:任务队列系统)
    • [🔄 Stack〈T〉实战示例:撤销重做系统](#🔄 Stack〈T〉实战示例:撤销重做系统)
    • [💡 关键差异总结](#💡 关键差异总结)
      • [Queue〈T〉 vs 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)

csharp 复制代码
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

csharp 复制代码
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数据绑定的核心

csharp 复制代码
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 - 线程安全的无序集合

csharp 复制代码
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> - 线程安全字典

csharp 复制代码
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 - 生产者消费者模式

csharp 复制代码
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>

csharp 复制代码
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 - 双向链表

csharp 复制代码
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. 集合初始化的正确方式

csharp 复制代码
// ❌ 不好的做法:多次调整容量
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. 避免装箱拆箱

csharp 复制代码
// ❌ 不好的做法:使用非泛型集合导致装箱
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较好

🚀 最佳实践速查表

初始化优化

csharp 复制代码
// ❌ 避免:未知容量时的多次扩容
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设计原则

csharp 复制代码
// ❌ 避免:暴露内部集合
public List<User> GetUsers() => _users;

// ✅ 推荐:返回只读接口
public IReadOnlyList<User> GetUsers() => _users.AsReadOnly();

// ✅ 更好:返回IEnumerable(完全封装)
public IEnumerable<User> GetActiveUsers() => _users.Where(u => u.IsActive);

线程安全选择

csharp 复制代码
// 单线程环境
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〉实战示例:任务队列系统

csharp 复制代码
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〉实战示例:撤销重做系统

csharp 复制代码
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>

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

相关推荐
会开花的二叉树2 小时前
上手 cpp-httplib:轻量级 C++ HTTP 库的安装与实战指南
开发语言·c++·http
balance_rui2 小时前
stm32进阶笔记
stm32·c#
秦禹辰2 小时前
开源多场景问答社区论坛Apache Answer本地部署并发布至公网使用
开发语言·后端·golang
代码村新手3 小时前
C语言-指针
c语言·开发语言·jvm
s9123601013 小时前
[rust] temporary value dropped while borrowed
开发语言·后端·rust
hbh112233abc3 小时前
PHP使用Imagick库操作tiff
开发语言·php
蓝莓味的口香糖3 小时前
【JS】JS基础-对象处理方法整合
开发语言·前端·javascript
欧的曼3 小时前
cygwin环境下php脚本异常中断后自动重启
开发语言·php
要做朋鱼燕3 小时前
ARM CoreSight:多核SoC调试追踪架构解析
开发语言·笔记·职场和发展·嵌入式·嵌入式软件