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 练习题
基础题
-
创建一个
List<int>,添加 10 个随机数,使用 LINQ 找出所有偶数和大于 50 的数。 -
使用
Dictionary<string, double>存储学生名字和成绩,实现添加、查找、删除、遍历功能。 -
使用
Queue<string>模拟打印机队列,添加打印任务,依次处理。
应用题
-
实现一个简单的统计程序:
-
从键盘输入多行文本
-
统计每个单词出现的次数(使用
Dictionary<string, int>) -
输出按出现次数排序的结果
-
-
实现一个 LRU(最近最少使用)缓存:
-
使用
LinkedList<T>和Dictionary<K, LinkedListNode<T>> -
容量限制
-
最近访问的元素移到头部
-
挑战题
-
实现一个泛型的
ObservableList<T>:-
继承
List<T> -
添加
ItemAdded、ItemRemoved、ItemChanged事件 -
在添加、删除、修改时触发事件
-
-
实现一个简单的图(Graph)数据结构:
-
使用
Dictionary<T, List<T>>存储邻接表 -
实现 BFS(广度优先搜索)和 DFS(深度优先搜索)
-
实现最短路径查找
-