C# 零基础到精通教程 - 第十章:集合与泛型——高效管理数据

10.1 为什么需要泛型?

10.1.1 没有泛型的问题

csharp

复制代码
// 问题1:装箱拆箱(性能问题)
ArrayList list = new ArrayList();
list.Add(10);        // int → object(装箱)
list.Add("Hello");   // string → object
int num = (int)list[0];  // object → int(拆箱,类型不安全)

// 问题2:类型不安全
list.Add(3.14);      // 不小心加了 double
foreach (int i in list)  // 运行时崩溃!
{
    Console.WriteLine(i);
}

10.1.2 泛型解决问题

csharp

复制代码
// 泛型:在编译时确定类型
List<int> numbers = new List<int>();
numbers.Add(10);     // ✅ 只能加 int
numbers.Add(20);
// numbers.Add("Hello");  // ❌ 编译错误!

int num = numbers[0];   // ✅ 不需要转换

// 类型安全 + 高性能(无装箱拆箱)
foreach (int i in numbers)
{
    Console.WriteLine(i);
}

10.1.3 泛型的基本概念

泛型 = 类型参数化。把类型作为参数,在使用时指定具体类型。

csharp

复制代码
// 泛型类:T 是类型参数
class Box<T>
{
    private T item;
    
    public void Put(T item)
    {
        this.item = item;
    }
    
    public T Get()
    {
        return item;
    }
}

// 使用
Box<int> intBox = new Box<int>();
intBox.Put(123);
int value = intBox.Get();  // 123

Box<string> strBox = new Box<string>();
strBox.Put("Hello");
string text = strBox.Get();  // "Hello"

10.2 泛型基础

10.2.1 泛型类

csharp

复制代码
// 单个类型参数
class GenericList<T>
{
    private T[] items;
    private int count;
    
    public GenericList(int capacity)
    {
        items = new T[capacity];
        count = 0;
    }
    
    public void Add(T item)
    {
        if (count < items.Length)
        {
            items[count] = item;
            count++;
        }
    }
    
    public T Get(int index)
    {
        if (index >= 0 && index < count)
            return items[index];
        throw new IndexOutOfRangeException();
    }
    
    public int Count => count;
}

// 多个类型参数
class Pair<TFirst, TSecond>
{
    public TFirst First { get; set; }
    public TSecond Second { get; set; }
    
    public Pair(TFirst first, TSecond second)
    {
        First = first;
        Second = second;
    }
    
    public void Print()
    {
        Console.WriteLine($"({First}, {Second})");
    }
}

// 使用
GenericList<int> intList = new GenericList<int>(10);
intList.Add(1);
intList.Add(2);
Console.WriteLine(intList.Get(0));  // 1

Pair<string, int> pair = new Pair<string, int>("年龄", 25);
pair.Print();  // (年龄, 25)

10.2.2 泛型方法

csharp

复制代码
class Utilities
{
    // 泛型方法:交换两个值
    public static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
    
    // 泛型方法:查找最大值
    public static T Max<T>(T a, T b) where T : IComparable<T>
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
    
    // 泛型方法:数组转列表
    public static List<T> ToList<T>(T[] array)
    {
        List<T> list = new List<T>();
        foreach (T item in array)
        {
            list.Add(item);
        }
        return list;
    }
}

// 使用
int x = 10, y = 20;
Utilities.Swap(ref x, ref y);
Console.WriteLine($"x={x}, y={y}");  // x=20, y=10

string s1 = "Apple", s2 = "Banana";
string max = Utilities.Max(s1, s2);
Console.WriteLine(max);  // Banana

int[] numbers = { 1, 2, 3 };
List<int> list = Utilities.ToList(numbers);

10.2.3 泛型接口

csharp

复制代码
// 定义泛型接口
interface IRepository<T>
{
    void Add(T item);
    T Get(int id);
    IEnumerable<T> GetAll();
    void Update(T item);
    void Delete(int id);
}

// 实现泛型接口
class InMemoryRepository<T> : IRepository<T> where T : IEntity
{
    private Dictionary<int, T> items = new Dictionary<int, T>();
    private int nextId = 1;
    
    public void Add(T item)
    {
        item.Id = nextId++;
        items[item.Id] = item;
    }
    
    public T Get(int id)
    {
        items.TryGetValue(id, out T item);
        return item;
    }
    
    public IEnumerable<T> GetAll()
    {
        return items.Values;
    }
    
    public void Update(T item)
    {
        if (items.ContainsKey(item.Id))
            items[item.Id] = item;
    }
    
    public void Delete(int id)
    {
        items.Remove(id);
    }
}

// 实体接口
interface IEntity
{
    int Id { get; set; }
}

// 具体实体
class Product : IEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    
    public override string ToString() => $"{Id}: {Name} - {Price:C}";
}

// 使用
IRepository<Product> repo = new InMemoryRepository<Product>();
repo.Add(new Product { Name = "iPhone", Price = 6999 });
repo.Add(new Product { Name = "iPad", Price = 3999 });

foreach (Product p in repo.GetAll())
{
    Console.WriteLine(p);
}

10.2.4 泛型约束(where)

约束:限制泛型类型参数必须满足的条件

csharp

复制代码
// 1. where T : struct  → 值类型约束
class ValueContainer<T> where T : struct
{
    public T Value { get; set; }
}
ValueContainer<int> vi = new ValueContainer<int>();  // ✅
// ValueContainer<string> vs = new ValueContainer<string>();  // ❌

// 2. where T : class  → 引用类型约束
class ReferenceContainer<T> where T : class
{
    public T Value { get; set; }
}
ReferenceContainer<string> rs = new ReferenceContainer<string>();  // ✅
// ReferenceContainer<int> ri = new ReferenceContainer<int>();  // ❌

// 3. where T : new()  → 必须有公共无参构造函数
class Factory<T> where T : new()
{
    public T Create()
    {
        return new T();  // 可以调用构造函数
    }
}
Factory<List<int>> factory = new Factory<List<int>>();
List<int> list = factory.Create();  // ✅

// 4. where T : 基类名  → 必须是指定基类或其派生类
class Animal { }
class Dog : Animal { }
class AnimalContainer<T> where T : Animal { }
AnimalContainer<Animal> ac1 = new AnimalContainer<Animal>();  // ✅
AnimalContainer<Dog> ac2 = new AnimalContainer<Dog>();        // ✅
// AnimalContainer<string> ac3 = new AnimalContainer<string>();  // ❌

// 5. where T : 接口名  → 必须实现指定接口
class DisposableContainer<T> where T : IDisposable { }
DisposableContainer<FileStream> dc = new DisposableContainer<FileStream>();  // ✅

// 6. 多个约束组合
class StrictContainer<T> where T : class, IComparable, new()
{
    // T 必须是引用类型,实现 IComparable,有无参构造
}

// 7. 多个类型参数的约束
class Pair<TKey, TValue> 
    where TKey : IComparable<TKey>
    where TValue : class, new()
{
    // ...
}

10.2.5 泛型委托

csharp

复制代码
// 定义泛型委托
delegate T Transformer<T>(T input);

class Program
{
    static int Square(int x) => x * x;
    static string ToUpper(string s) => s.ToUpper();
    
    static void Main()
    {
        Transformer<int> intTransformer = Square;
        Console.WriteLine(intTransformer(5));  // 25
        
        Transformer<string> stringTransformer = ToUpper;
        Console.WriteLine(stringTransformer("hello"));  // HELLO
        
        // 使用内置泛型委托
        Func<int, int> square = x => x * x;
        Action<string> print = s => Console.WriteLine(s);
        Predicate<int> isEven = n => n % 2 == 0;
    }
}

10.3 常用泛型集合

10.3.1 List<T>------动态数组

最常用的集合,像数组一样有序存储,但可以动态增减

csharp

复制代码
using System.Collections.Generic;

// 创建
List<int> numbers = new List<int>();
List<string> names = new List<string> { "张三", "李四", "王五" };

// 添加元素
numbers.Add(10);
numbers.Add(20);
numbers.AddRange(new int[] { 30, 40, 50 });  // 批量添加

// 插入元素
numbers.Insert(1, 15);      // 在索引1插入15
numbers.InsertRange(2, new int[] { 16, 17 });  // 批量插入

// 访问元素
int first = numbers[0];     // 通过索引访问
int last = numbers[^1];     // 倒数第一个(C# 8.0+)

// 修改元素
numbers[0] = 100;

// 删除元素
numbers.Remove(20);          // 删除值为20的第一个元素
numbers.RemoveAt(0);         // 删除索引0的元素
numbers.RemoveRange(1, 3);   // 删除从索引1开始的3个元素
numbers.Clear();             // 清空所有

// 查找元素
bool contains = numbers.Contains(30);
int index = numbers.IndexOf(30);      // 第一次出现的位置
int lastIndex = numbers.LastIndexOf(30);  // 最后一次出现的位置

// 排序和反转
numbers.Sort();              // 升序排序
numbers.Reverse();           // 反转顺序

// 其他常用方法
int count = numbers.Count;   // 元素个数(不是 Capacity)
numbers.Capacity = 100;      // 设置容量
numbers.TrimExcess();        // 释放多余容量

// 遍历
foreach (int num in numbers)
{
    Console.WriteLine(num);
}

for (int i = 0; i < numbers.Count; i++)
{
    Console.WriteLine(numbers[i]);
}

10.3.2 Dictionary<TKey, TValue>------键值对集合

通过键(Key)快速查找值(Value),像字典查单词

csharp

复制代码
// 创建字典
Dictionary<string, int> ages = new Dictionary<string, int>();
Dictionary<int, string> students = new Dictionary<int, string>
{
    { 1001, "张三" },
    { 1002, "李四" },
    { 1003, "王五" }
};

// 添加元素
ages.Add("张三", 25);
ages.Add("李四", 30);
ages["王五"] = 28;  // 如果键存在则修改,不存在则添加

// 访问元素(注意:键不存在会抛异常)
int age = ages["张三"];

// 安全访问
if (ages.TryGetValue("赵六", out int zhaoAge))
{
    Console.WriteLine(zhaoAge);
}
else
{
    Console.WriteLine("未找到");
}

// 检查键是否存在
bool hasKey = ages.ContainsKey("张三");
bool hasValue = ages.ContainsValue(25);

// 修改元素
ages["张三"] = 26;

// 删除元素
ages.Remove("李四");

// 遍历
// 遍历键值对
foreach (KeyValuePair<string, int> kvp in ages)
{
    Console.WriteLine($"{kvp.Key} : {kvp.Value}");
}

// 只遍历键
foreach (string name in ages.Keys)
{
    Console.WriteLine(name);
}

// 只遍历值
foreach (int ageValue in ages.Values)
{
    Console.WriteLine(ageValue);
}

// 属性
int count = ages.Count;

10.3.3 HashSet<T>------唯一值集合

存储不重复的元素,查找速度快

csharp

复制代码
// 创建
HashSet<int> numbers = new HashSet<int>();
HashSet<string> names = new HashSet<string> { "张三", "李四", "王五" };

// 添加(重复添加无效)
numbers.Add(1);   // true
numbers.Add(2);   // true
numbers.Add(1);   // false(已存在)

// 删除
numbers.Remove(2);

// 查找
bool contains = numbers.Contains(1);

// 集合运算
HashSet<int> set1 = new HashSet<int> { 1, 2, 3, 4, 5 };
HashSet<int> set2 = new HashSet<int> { 4, 5, 6, 7, 8 };

// 并集
set1.UnionWith(set2);      // set1 = {1,2,3,4,5,6,7,8}

// 交集
set1.IntersectWith(set2);  // set1 = {4,5}

// 差集(set1 中有但 set2 中没有)
set1.ExceptWith(set2);     // set1 = {1,2,3}

// 对称差(只在一个集合中的元素)
set1.SymmetricExceptWith(set2);

// 子集/超集判断
bool isSubset = set1.IsSubsetOf(set2);
bool isSuperset = set1.IsSupersetOf(set2);

10.3.4 Queue<T>------队列(先进先出 FIFO)

csharp

复制代码
// 创建队列
Queue<string> queue = new Queue<string>();

// 入队
queue.Enqueue("第一");
queue.Enqueue("第二");
queue.Enqueue("第三");

// 出队(移除并返回)
string first = queue.Dequeue();  // "第一"

// 查看队首(不移除)
string peek = queue.Peek();      // "第二"

// 其他
int count = queue.Count;
bool contains = queue.Contains("第二");
queue.Clear();

// 遍历(不会改变队列)
foreach (string item in queue)
{
    Console.WriteLine(item);
}

10.3.5 Stack<T>------栈(后进先出 LIFO)

csharp

复制代码
// 创建栈
Stack<string> stack = new Stack<string>();

// 压栈
stack.Push("第一");
stack.Push("第二");
stack.Push("第三");

// 出栈(移除并返回)
string last = stack.Pop();   // "第三"

// 查看栈顶(不移除)
string peek = stack.Peek();  // "第二"

// 其他
int count = stack.Count;
bool contains = stack.Contains("第一");

// 遍历(不会改变栈)
foreach (string item in stack)
{
    Console.WriteLine(item);  // 从栈顶到栈底
}

10.3.6 LinkedList<T>------链表

适合频繁插入删除的场景

csharp

复制代码
// 创建链表
LinkedList<string> list = new LinkedList<string>();

// 添加
LinkedListNode<string> first = list.AddFirst("第一个");
LinkedListNode<string> last = list.AddLast("最后一个");
LinkedListNode<string> node = list.AddAfter(first, "第二个");
list.AddBefore(last, "倒数第二个");

// 删除
list.Remove("第二个");
list.RemoveFirst();
list.RemoveLast();

// 查找
LinkedListNode<string> found = list.Find("第一个");

// 遍历
foreach (string item in list)
{
    Console.WriteLine(item);
}

// 从后往前遍历
for (LinkedListNode<string> node2 = list.Last; node2 != null; node2 = node2.Previous)
{
    Console.WriteLine(node2.Value);
}

10.4 常用集合对比

10.4.1 性能对比

集合类型 添加 查找 删除 有序 唯一 适用场景
List<T> O(1)* O(n) O(n) 一般用途,索引访问
Dictionary<K,V> O(1) O(1) O(1) Key唯一 键值对查找
HashSet<T> O(1) O(1) O(1) 去重、集合运算
Queue<T> O(1) - O(1) 先进先出
Stack<T> O(1) - O(1) 后进先出
LinkedList<T> O(1)* O(n) O(1)* 频繁插入删除

*O(1) 表示常数时间,O(n) 表示线性时间

10.4.2 选择指南

csharp

复制代码
// 场景1:存储学生名单,需要按顺序访问
List<string> students = new List<string>();

// 场景2:通过学号快速查找学生
Dictionary<int, Student> studentDict = new Dictionary<int, Student>();

// 场景3:去除重复的IP地址
HashSet<string> uniqueIPs = new HashSet<string>();

// 场景4:消息队列
Queue<Message> messageQueue = new Queue<Message>();

// 场景5:撤销操作(Undo)
Stack<Command> undoStack = new Stack<Command>();

10.5 集合的常用操作

10.5.1 LINQ 查询(基础)

LINQ 让集合查询更简洁(第十一章会详细讲)

csharp

复制代码
using System.Linq;

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 筛选
var evens = numbers.Where(n => n % 2 == 0);
// 结果:2,4,6,8,10

// 投影
var squares = numbers.Select(n => n * n);
// 结果:1,4,9,16,25,36,49,64,81,100

// 排序
var sortedDesc = numbers.OrderByDescending(n => n);

// 聚合
int sum = numbers.Sum();
double avg = numbers.Average();
int max = numbers.Max();
int min = numbers.Min();

// 取前几个
var first3 = numbers.Take(3);      // 1,2,3
var skip3 = numbers.Skip(3);       // 4,5,6,7,8,9,10

// 去重
List<int> withDuplicates = new List<int> { 1, 2, 2, 3, 3, 3 };
var distinct = withDuplicates.Distinct();  // 1,2,3

10.5.2 集合转换

csharp

复制代码
// List ↔ 数组
List<int> list = new List<int> { 1, 2, 3 };
int[] array = list.ToArray();
List<int> newList = array.ToList();

// List ↔ HashSet
HashSet<int> set = new HashSet<int>(list);
List<int> backToList = set.ToList();

// Dictionary ↔ List
Dictionary<string, int> dict = new Dictionary<string, int>();
List<KeyValuePair<string, int>> kvpList = dict.ToList();
List<string> keys = dict.Keys.ToList();
List<int> values = dict.Values.ToList();

10.5.3 自定义比较器

csharp

复制代码
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 自定义排序比较器
class PersonAgeComparer : IComparer<Person>
{
    public int Compare(Person x, Person y)
    {
        return x.Age.CompareTo(y.Age);
    }
}

// 自定义相等比较器
class PersonNameComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name;
    }
    
    public int GetHashCode(Person obj)
    {
        return obj.Name.GetHashCode();
    }
}

// 使用
List<Person> people = new List<Person>
{
    new Person { Name = "张三", Age = 25 },
    new Person { Name = "李四", Age = 20 },
    new Person { Name = "王五", Age = 30 }
};

people.Sort(new PersonAgeComparer());  // 按年龄排序

HashSet<Person> uniquePeople = new HashSet<Person>(new PersonNameComparer());

10.6 综合示例

示例1:学生成绩管理系统(集合版)

csharp

复制代码
using System;
using System.Collections.Generic;
using System.Linq;

class Student
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Class { get; set; }
    public Dictionary<string, double> Scores { get; set; }
    
    public Student(string id, string name, string className)
    {
        Id = id;
        Name = name;
        Class = className;
        Scores = new Dictionary<string, double>();
    }
    
    public double GetAverageScore()
    {
        if (Scores.Count == 0) return 0;
        return Scores.Values.Average();
    }
    
    public double GetTotalScore()
    {
        return Scores.Values.Sum();
    }
    
    public override string ToString()
    {
        return $"{Id} | {Name} | {Class} | 平均分:{GetAverageScore():F1}";
    }
}

class ScoreManager
{
    private Dictionary<string, Student> students = new Dictionary<string, Student>();
    private List<string> subjects = new List<string>();
    
    public void AddStudent(string id, string name, string className)
    {
        if (students.ContainsKey(id))
        {
            Console.WriteLine($"学生 {id} 已存在");
            return;
        }
        
        students[id] = new Student(id, name, className);
        Console.WriteLine($"已添加学生:{name} ({id})");
    }
    
    public void AddScore(string studentId, string subject, double score)
    {
        if (!students.TryGetValue(studentId, out Student student))
        {
            Console.WriteLine($"未找到学生 {studentId}");
            return;
        }
        
        student.Scores[subject] = score;
        
        if (!subjects.Contains(subject))
            subjects.Add(subject);
        
        Console.WriteLine($"已添加成绩:{student.Name} - {subject}: {score}");
    }
    
    public void ShowAllStudents()
    {
        Console.WriteLine("\n=== 所有学生 ===");
        foreach (var student in students.Values)
        {
            Console.WriteLine(student);
        }
    }
    
    public void ShowClassStatistics(string className)
    {
        var classStudents = students.Values.Where(s => s.Class == className).ToList();
        
        if (classStudents.Count == 0)
        {
            Console.WriteLine($"未找到班级 {className}");
            return;
        }
        
        Console.WriteLine($"\n=== {className} 班统计 ===");
        Console.WriteLine($"班级人数:{classStudents.Count}");
        
        // 班级平均分
        double classAvg = classStudents.Average(s => s.GetAverageScore());
        Console.WriteLine($"班级平均分:{classAvg:F1}");
        
        // 各科平均分
        Console.WriteLine("\n各科平均分:");
        foreach (string subject in subjects)
        {
            double subjectAvg = classStudents
                .Where(s => s.Scores.ContainsKey(subject))
                .Average(s => s.Scores[subject]);
            Console.WriteLine($"  {subject}: {subjectAvg:F1}");
        }
        
        // 排名
        var ranked = classStudents.OrderByDescending(s => s.GetAverageScore()).ToList();
        Console.WriteLine("\n班级排名:");
        for (int i = 0; i < ranked.Count; i++)
        {
            Console.WriteLine($"  {i + 1}. {ranked[i].Name} - {ranked[i].GetAverageScore():F1}");
        }
    }
    
    public void SearchStudent(string keyword)
    {
        var results = students.Values
            .Where(s => s.Name.Contains(keyword) || s.Id.Contains(keyword))
            .ToList();
        
        if (results.Count == 0)
        {
            Console.WriteLine($"未找到包含 '{keyword}' 的学生");
            return;
        }
        
        Console.WriteLine($"\n搜索结果(共{results.Count}人):");
        foreach (var student in results)
        {
            Console.WriteLine($"  {student}");
        }
    }
    
    public void GetStudentDetail(string studentId)
    {
        if (!students.TryGetValue(studentId, out Student student))
        {
            Console.WriteLine($"未找到学生 {studentId}");
            return;
        }
        
        Console.WriteLine($"\n=== 学生详情 ===");
        Console.WriteLine($"学号:{student.Id}");
        Console.WriteLine($"姓名:{student.Name}");
        Console.WriteLine($"班级:{student.Class}");
        Console.WriteLine($"总分:{student.GetTotalScore()}");
        Console.WriteLine($"平均分:{student.GetAverageScore():F1}");
        Console.WriteLine("\n各科成绩:");
        foreach (var score in student.Scores)
        {
            Console.WriteLine($"  {score.Key}: {score.Value}");
        }
    }
    
    public void GetTopN(int n)
    {
        var top = students.Values
            .OrderByDescending(s => s.GetAverageScore())
            .Take(n)
            .ToList();
        
        Console.WriteLine($"\n=== 全年级前{n}名 ===");
        for (int i = 0; i < top.Count; i++)
        {
            Console.WriteLine($"{i + 1}. {top[i].Name} ({top[i].Class}) - {top[i].GetAverageScore():F1}");
        }
    }
}

class Program
{
    static void Main()
    {
        ScoreManager manager = new ScoreManager();
        
        // 添加学生
        manager.AddStudent("001", "张三", "高一1班");
        manager.AddStudent("002", "李四", "高一1班");
        manager.AddStudent("003", "王五", "高一2班");
        manager.AddStudent("004", "赵六", "高一2班");
        manager.AddStudent("005", "小明", "高一1班");
        
        // 添加成绩
        manager.AddScore("001", "数学", 85);
        manager.AddScore("001", "语文", 78);
        manager.AddScore("001", "英语", 92);
        
        manager.AddScore("002", "数学", 92);
        manager.AddScore("002", "语文", 88);
        manager.AddScore("002", "英语", 85);
        
        manager.AddScore("003", "数学", 76);
        manager.AddScore("003", "语文", 82);
        manager.AddScore("003", "英语", 79);
        
        manager.AddScore("004", "数学", 95);
        manager.AddScore("004", "语文", 91);
        manager.AddScore("004", "英语", 88);
        
        manager.AddScore("005", "数学", 68);
        manager.AddScore("005", "语文", 72);
        manager.AddScore("005", "英语", 70);
        
        // 显示
        manager.ShowAllStudents();
        manager.ShowClassStatistics("高一1班");
        manager.ShowClassStatistics("高一2班");
        manager.GetTopN(3);
        manager.SearchStudent("小");
        manager.GetStudentDetail("001");
    }
}

示例2:购物车系统(Dictionary + List)

csharp

复制代码
using System;
using System.Collections.Generic;
using System.Linq;

class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    public int Stock { get; set; }
    
    public Product(string id, string name, double price, int stock)
    {
        Id = id;
        Name = name;
        Price = price;
        Stock = stock;
    }
    
    public override string ToString()
    {
        return $"{Name} - {Price:C} (库存:{Stock})";
    }
}

class CartItem
{
    public Product Product { get; set; }
    public int Quantity { get; set; }
    
    public double Subtotal => Product.Price * Quantity;
}

class ShoppingCart
{
    private Dictionary<string, CartItem> items = new Dictionary<string, CartItem>();
    
    public void AddProduct(Product product, int quantity = 1)
    {
        if (quantity <= 0) return;
        
        if (items.ContainsKey(product.Id))
        {
            items[product.Id].Quantity += quantity;
        }
        else
        {
            items[product.Id] = new CartItem { Product = product, Quantity = quantity };
        }
        
        Console.WriteLine($"已添加 {quantity} x {product.Name} 到购物车");
    }
    
    public void RemoveProduct(string productId, int quantity = -1)
    {
        if (!items.ContainsKey(productId))
        {
            Console.WriteLine("商品不在购物车中");
            return;
        }
        
        var item = items[productId];
        
        if (quantity < 0 || quantity >= item.Quantity)
        {
            items.Remove(productId);
            Console.WriteLine($"已移除 {item.Product.Name}");
        }
        else
        {
            item.Quantity -= quantity;
            Console.WriteLine($"已从 {item.Product.Name} 中移除 {quantity} 件");
        }
    }
    
    public void UpdateQuantity(string productId, int quantity)
    {
        if (!items.ContainsKey(productId))
        {
            Console.WriteLine("商品不在购物车中");
            return;
        }
        
        if (quantity <= 0)
        {
            items.Remove(productId);
            Console.WriteLine($"已移除 {items[productId].Product.Name}");
        }
        else
        {
            items[productId].Quantity = quantity;
            Console.WriteLine($"已更新 {items[productId].Product.Name} 数量为 {quantity}");
        }
    }
    
    public void ShowCart()
    {
        if (items.Count == 0)
        {
            Console.WriteLine("购物车是空的");
            return;
        }
        
        Console.WriteLine("\n=== 购物车 ===");
        Console.WriteLine("商品名称\t单价\t数量\t小计");
        Console.WriteLine("----------------------------------------");
        
        double total = 0;
        foreach (var item in items.Values)
        {
            Console.WriteLine($"{item.Product.Name}\t{item.Product.Price:C}\t{item.Quantity}\t{item.Subtotal:C}");
            total += item.Subtotal;
        }
        
        Console.WriteLine("----------------------------------------");
        Console.WriteLine($"总计:{total:C}");
    }
    
    public double GetTotal() => items.Values.Sum(i => i.Subtotal);
    
    public int GetItemCount() => items.Values.Sum(i => i.Quantity);
    
    public void Clear() => items.Clear();
}

class Order
{
    public string OrderId { get; set; }
    public DateTime OrderTime { get; set; }
    public List<CartItem> Items { get; set; }
    public double TotalAmount { get; set; }
    public string Status { get; set; }
    
    public void ShowOrder()
    {
        Console.WriteLine($"\n订单号:{OrderId}");
        Console.WriteLine($"下单时间:{OrderTime}");
        Console.WriteLine($"订单状态:{Status}");
        Console.WriteLine($"总金额:{TotalAmount:C}");
        Console.WriteLine("商品列表:");
        foreach (var item in Items)
        {
            Console.WriteLine($"  {item.Product.Name} x {item.Quantity} = {item.Subtotal:C}");
        }
    }
}

class OrderManager
{
    private List<Order> orders = new List<Order>();
    private int nextOrderId = 1000;
    
    public Order CreateOrder(ShoppingCart cart, string paymentMethod)
    {
        if (cart.GetItemCount() == 0)
        {
            Console.WriteLine("购物车是空的,无法下单");
            return null;
        }
        
        Order order = new Order
        {
            OrderId = $"ORD{nextOrderId++}",
            OrderTime = DateTime.Now,
            Items = new List<CartItem>(cart.GetAllItems()),  // 需要添加 GetItems 方法
            TotalAmount = cart.GetTotal(),
            Status = "已下单"
        };
        
        orders.Add(order);
        cart.Clear();
        
        Console.WriteLine($"订单创建成功!订单号:{order.OrderId}");
        return order;
    }
    
    public void ShowAllOrders()
    {
        if (orders.Count == 0)
        {
            Console.WriteLine("暂无订单");
            return;
        }
        
        Console.WriteLine("\n=== 历史订单 ===");
        foreach (var order in orders)
        {
            order.ShowOrder();
        }
    }
}

// 需要添加到 ShoppingCart 的方法
partial class ShoppingCart
{
    public List<CartItem> GetAllItems()
    {
        return items.Values.ToList();
    }
}

class Program
{
    static void Main()
    {
        // 商品库
        Dictionary<string, Product> products = new Dictionary<string, Product>
        {
            { "P001", new Product("P001", "iPhone 15", 5999, 10) },
            { "P002", new Product("P002", "MacBook Pro", 14999, 5) },
            { "P003", new Product("P003", "AirPods Pro", 1899, 20) },
            { "P004", new Product("P004", "iPad Air", 4799, 8) },
            { "P005", new Product("P005", "Apple Watch", 3199, 15) }
        };
        
        ShoppingCart cart = new ShoppingCart();
        OrderManager orderManager = new OrderManager();
        
        bool running = true;
        
        while (running)
        {
            Console.WriteLine("\n=== 商城系统 ===");
            Console.WriteLine("1. 查看商品");
            Console.WriteLine("2. 添加到购物车");
            Console.WriteLine("3. 查看购物车");
            Console.WriteLine("4. 修改购物车");
            Console.WriteLine("5. 下单");
            Console.WriteLine("6. 查看订单");
            Console.WriteLine("7. 退出");
            Console.Write("请选择:");
            
            string choice = Console.ReadLine();
            
            switch (choice)
            {
                case "1":
                    Console.WriteLine("\n=== 商品列表 ===");
                    foreach (var product in products.Values)
                    {
                        Console.WriteLine($"{product.Id} - {product}");
                    }
                    break;
                    
                case "2":
                    Console.Write("请输入商品ID:");
                    string id = Console.ReadLine();
                    if (products.TryGetValue(id, out Product product))
                    {
                        Console.Write("请输入数量:");
                        if (int.TryParse(Console.ReadLine(), out int qty) && qty > 0)
                        {
                            if (qty <= product.Stock)
                            {
                                cart.AddProduct(product, qty);
                            }
                            else
                            {
                                Console.WriteLine($"库存不足,最多可买 {product.Stock} 件");
                            }
                        }
                        else
                        {
                            Console.WriteLine("数量无效");
                        }
                    }
                    else
                    {
                        Console.WriteLine("商品不存在");
                    }
                    break;
                    
                case "3":
                    cart.ShowCart();
                    break;
                    
                case "4":
                    cart.ShowCart();
                    Console.Write("请输入要修改的商品ID:");
                    string modifyId = Console.ReadLine();
                    Console.Write("请输入新数量(0表示删除):");
                    if (int.TryParse(Console.ReadLine(), out int newQty))
                    {
                        cart.UpdateQuantity(modifyId, newQty);
                    }
                    break;
                    
                case "5":
                    if (cart.GetItemCount() > 0)
                    {
                        cart.ShowCart();
                        Console.Write("请选择支付方式(1.微信 2.支付宝 3.银行卡):");
                        string payment = Console.ReadLine();
                        orderManager.CreateOrder(cart, payment);
                    }
                    else
                    {
                        Console.WriteLine("购物车为空");
                    }
                    break;
                    
                case "6":
                    orderManager.ShowAllOrders();
                    break;
                    
                case "7":
                    running = false;
                    Console.WriteLine("感谢光临!");
                    break;
                    
                default:
                    Console.WriteLine("无效选择");
                    break;
            }
        }
    }
}

示例3:任务管理器(Queue + Stack)

csharp

复制代码
using System;
using System.Collections.Generic;
using System.Threading;

class Task
{
    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime CreateTime { get; set; }
    public Action Action { get; set; }
    
    public Task(string id, string name, Action action)
    {
        Id = id;
        Name = name;
        CreateTime = DateTime.Now;
        Action = action;
    }
    
    public void Execute()
    {
        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 执行任务:{Name}");
        Action?.Invoke();
    }
    
    public override string ToString()
    {
        return $"{Id} - {Name} ({CreateTime:HH:mm:ss})";
    }
}

class TaskQueue
{
    private Queue<Task> pendingTasks = new Queue<Task>();
    private Stack<Task> completedTasks = new Stack<Task>();
    private Stack<Task> undoneTasks = new Stack<Task>();  // 已撤销的任务
    
    public void AddTask(Task task)
    {
        pendingTasks.Enqueue(task);
        Console.WriteLine($"已添加任务:{task.Name}");
    }
    
    public void AddTasks(params Task[] tasks)
    {
        foreach (var task in tasks)
        {
            AddTask(task);
        }
    }
    
    public void ProcessNext()
    {
        if (pendingTasks.Count == 0)
        {
            Console.WriteLine("没有待处理的任务");
            return;
        }
        
        Task task = pendingTasks.Dequeue();
        task.Execute();
        
        completedTasks.Push(task);
        undoneTasks.Clear();  // 新任务完成后,清空撤销栈
    }
    
    public void ProcessAll()
    {
        while (pendingTasks.Count > 0)
        {
            ProcessNext();
            Thread.Sleep(500);
        }
        Console.WriteLine("所有任务处理完毕");
    }
    
    public void Undo()
    {
        if (completedTasks.Count == 0)
        {
            Console.WriteLine("没有可撤销的任务");
            return;
        }
        
        Task task = completedTasks.Pop();
        undoneTasks.Push(task);
        Console.WriteLine($"已撤销任务:{task.Name}");
        
        // 模拟撤销操作
        Console.WriteLine($"  撤销:{task.Name}");
    }
    
    public void Redo()
    {
        if (undoneTasks.Count == 0)
        {
            Console.WriteLine("没有可重做的任务");
            return;
        }
        
        Task task = undoneTasks.Pop();
        completedTasks.Push(task);
        Console.WriteLine($"已重做任务:{task.Name}");
        
        // 重新执行
        task.Execute();
    }
    
    public void ShowStatus()
    {
        Console.WriteLine("\n=== 任务状态 ===");
        Console.WriteLine($"待处理任务:{pendingTasks.Count}");
        Console.WriteLine($"已完成任务:{completedTasks.Count}");
        Console.WriteLine($"可撤销任务:{undoneTasks.Count}");
        
        if (pendingTasks.Count > 0)
        {
            Console.WriteLine("\n下一个任务:");
            Console.WriteLine($"  {pendingTasks.Peek()}");
        }
        
        if (completedTasks.Count > 0)
        {
            Console.WriteLine("\n最近完成的任务:");
            Console.WriteLine($"  {completedTasks.Peek()}");
        }
    }
}

class Program
{
    static void Main()
    {
        TaskQueue taskQueue = new TaskQueue();
        
        // 创建任务
        Task task1 = new Task("T001", "发送邮件", () => 
            Console.WriteLine("  📧 邮件发送成功"));
        
        Task task2 = new Task("T002", "备份数据库", () =>
            Console.WriteLine("  💾 数据库备份完成"));
        
        Task task3 = new Task("T003", "生成报表", () =>
            Console.WriteLine("  📊 报表生成完成"));
        
        Task task4 = new Task("T004", "清理缓存", () =>
            Console.WriteLine("  🧹 缓存清理完成"));
        
        // 添加任务
        taskQueue.AddTasks(task1, task2, task3, task4);
        taskQueue.ShowStatus();
        
        Console.WriteLine("\n=== 处理任务 ===");
        taskQueue.ProcessNext();  // 发送邮件
        taskQueue.ProcessNext();  // 备份数据库
        taskQueue.ShowStatus();
        
        Console.WriteLine("\n=== 撤销测试 ===");
        taskQueue.Undo();  // 撤销备份数据库
        taskQueue.ShowStatus();
        
        Console.WriteLine("\n=== 重做测试 ===");
        taskQueue.Redo();  // 重做备份数据库
        taskQueue.ShowStatus();
        
        Console.WriteLine("\n=== 处理剩余任务 ===");
        taskQueue.ProcessAll();
        taskQueue.ShowStatus();
    }
}

10.7 常见错误与陷阱

错误1:Dictionary 使用不存在的键

csharp

复制代码
Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("a", 1);

int value = dict["b"];  // ❌ KeyNotFoundException

// 正确
if (dict.ContainsKey("b"))
    value = dict["b"];

// 或
if (dict.TryGetValue("b", out value))
    Console.WriteLine(value);

错误2:List 遍历时修改

csharp

复制代码
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// ❌ 错误:遍历时删除会出错
foreach (int n in numbers)
{
    if (n % 2 == 0)
        numbers.Remove(n);  // InvalidOperationException
}

// ✅ 正确:倒序遍历删除
for (int i = numbers.Count - 1; i >= 0; i--)
{
    if (numbers[i] % 2 == 0)
        numbers.RemoveAt(i);
}

// 或使用 RemoveAll
numbers.RemoveAll(n => n % 2 == 0);

错误3:值类型和引用类型混淆

csharp

复制代码
// Dictionary 中的值类型修改陷阱
Dictionary<string, Point> points = new Dictionary<string, Point>();
points["p1"] = new Point { X = 1, Y = 2 };

// ❌ 错误:不能直接修改值类型的属性
points["p1"].X = 10;  // 编译错误

// ✅ 正确:取出、修改、放回
Point p = points["p1"];
p.X = 10;
points["p1"] = p;

struct Point { public int X; public int Y; }

错误4:自定义类的相等比较

csharp

复制代码
class Person
{
    public string Name { get; set; }
}

HashSet<Person> people = new HashSet<Person>();
people.Add(new Person { Name = "张三" });
people.Add(new Person { Name = "张三" });  // 会添加成功,因为是不同的对象

// 需要重写 Equals 和 GetHashCode
class Person2
{
    public string Name { get; set; }
    
    public override bool Equals(object obj)
    {
        return obj is Person2 other && other.Name == Name;
    }
    
    public override int GetHashCode()
    {
        return Name?.GetHashCode() ?? 0;
    }
}

10.8 本章总结

核心知识点导图

text

复制代码
集合与泛型
├── 泛型基础
│   ├── 泛型类
│   ├── 泛型方法
│   ├── 泛型接口
│   └── 泛型约束
│
├── 常用集合
│   ├── List<T> → 动态数组
│   ├── Dictionary<K,V> → 键值对
│   ├── HashSet<T> → 唯一值
│   ├── Queue<T> → 先进先出
│   └── Stack<T> → 后进先出
│
└── 选择指南
    ├── 需要索引 → List
    ├── 需要通过键查找 → Dictionary
    ├── 需要去重 → HashSet
    ├── 先进先出 → Queue
    ├── 后进先出 → Stack
    └── 频繁插入删除 → LinkedList

集合性能对比

操作 List Dictionary HashSet Queue/Stack
添加 O(1)* O(1) O(1) O(1)
查找 O(n) O(1) O(1) -
删除 O(n) O(1) O(1) O(1)
按索引 O(1) - - -

*需要扩容时 O(n)


10.9 练习题

基础题

  1. 创建一个 List<int>,添加 10 个随机数,使用 LINQ 找出所有偶数和大于 50 的数。

  2. 使用 Dictionary<string, double> 存储学生名字和成绩,实现添加、查找、删除、遍历功能。

  3. 使用 Queue<string> 模拟打印机队列,添加打印任务,依次处理。

应用题

  1. 实现一个简单的统计程序:

    • 从键盘输入多行文本

    • 统计每个单词出现的次数(使用 Dictionary<string, int>

    • 输出按出现次数排序的结果

  2. 实现一个 LRU(最近最少使用)缓存:

    • 使用 LinkedList<T>Dictionary<K, LinkedListNode<T>>

    • 容量限制

    • 最近访问的元素移到头部

挑战题

  1. 实现一个泛型的 ObservableList<T>

    • 继承 List<T>

    • 添加 ItemAddedItemRemovedItemChanged 事件

    • 在添加、删除、修改时触发事件

  2. 实现一个简单的图(Graph)数据结构:

    • 使用 Dictionary<T, List<T>> 存储邻接表

    • 实现 BFS(广度优先搜索)和 DFS(深度优先搜索)

    • 实现最短路径查找

相关推荐
ch.ju5 小时前
Java Programming Chapter 4——Composition of classes
java·开发语言
人道领域5 小时前
Java基础热门八股总结:八种基本数据类型 + 装箱拆箱 + 缓存机制,(90%的Java新手都搞不清的装箱拆箱问题)
java·开发语言·python
Deep-w6 小时前
【MATLAB】含光伏 - 储能的家庭/工业微电网能量管理仿真研究
开发语言·算法·matlab
菜鸟小九6 小时前
JUC补充(ThreadLocal、completableFuture)
java·开发语言
dyxal6 小时前
Louvain 算法:让网络自己“报团取暖”的发现者
开发语言·算法
计算机安禾6 小时前
【c++面向对象编程】第41篇:函数模板与类模板:泛型编程的基石
开发语言·c++·算法
熊猫_豆豆6 小时前
麦克斯韦方程组(电磁效应Python展示)
开发语言·python·电磁感应·麦克斯韦方程组
SilentSamsara6 小时前
属性查找顺序:实例 → 类 → 父类的完整 MRO
开发语言·python·算法·青少年编程
运维行者_7 小时前
云计算连接性与互操作性
服务器·开发语言·网络·web安全·网络基础设施