home commit with new plugins

This commit is contained in:
2026-06-21 09:51:55 +08:00
parent ebb9e385c5
commit 3d067fe75c
85 changed files with 10794 additions and 1443 deletions
+307
View File
@@ -0,0 +1,307 @@
# ArrayList
概念:
`ArrayList` 是 C# 提供的一个==非泛型集合类==
本质上可以理解成:==一个长度可变的 `object` 数组==
它可以:
1. 像数组一样按下标访问
2. 自动扩容
3. 增删查改元素
4. 存储任意类型的对象
命名空间:
```csharp
using System.Collections;
```
注意:
`System.Collections`
不是 `System.Collection`
----
# 通常用法
1. 学习 C# 早期非泛型集合
2. 理解 `object`、装箱和拆箱
3. 存储数量不固定的数据
4. 对数组进行更方便的增删操作
实际开发中:
新代码更推荐使用 `List<T>`
`ArrayList` 主要作为进阶阶段理解集合历史和装箱拆箱的材料
----
# 语法
创建:
```csharp
ArrayList list = new ArrayList();
```
添加:
```csharp
list.Add();
```
访问:
```csharp
list[]
```
长度:
```csharp
list.Count
```
----
# 基础案例
```csharp
using System;
using System.Collections;
ArrayList list = new ArrayList();
list.Add("小明");
list.Add(18);
list.Add(true);
Console.WriteLine(list[0]);
Console.WriteLine(list[1]);
Console.WriteLine(list[2]);
Console.WriteLine(list.Count);
```
理解:
1. `ArrayList` 内部存的是 `object`
2. 所以字符串、整数、布尔值都可以放进去
3. `Count` 表示当前实际存了多少个元素
----
# 增删查改
增加:
```csharp
ArrayList list = new ArrayList();
list.Add("A");
list.Add("B");
list.Insert(1, "C");
```
结果:
```text
A C B
```
删除:
```csharp
list.Remove("A"); // 删除第一个匹配的元素
list.RemoveAt(0); // 删除指定下标的元素
list.Clear(); // 清空所有元素
```
查找:
```csharp
bool hasB = list.Contains("B");
int index = list.IndexOf("B");
```
修改:
```csharp
list[0] = "新的值";
```
----
# 遍历
使用 `for`
```csharp
ArrayList list = new ArrayList();
list.Add("A");
list.Add("B");
list.Add("C");
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
```
使用 `foreach`
```csharp
foreach (object item in list)
{
Console.WriteLine(item);
}
```
因为 `ArrayList` 中的元素都是按 `object` 看待,所以 `foreach` 变量常写成 `object`
----
# 取值时要类型转换
```csharp
ArrayList list = new ArrayList();
list.Add(100);
int num = (int)list[0];
Console.WriteLine(num);
```
原因:
`list[0]` 的返回类型是 `object`
如果要当成 `int` 使用,就需要拆箱 / 强制类型转换
错误示意:
```csharp
int num = list[0];
```
这样会报错,因为不能把 `object` 直接赋值给 `int`
----
# 装箱和拆箱问题
```csharp
ArrayList list = new ArrayList();
int hp = 100;
list.Add(hp); // int 放进 object,发生装箱
int value = (int)list[0]; // object 取回 int,发生拆箱
```
理解:
1. 值类型放进 `ArrayList` 时,会发生装箱
2.`ArrayList` 取出值类型时,需要拆箱
3. 装箱和拆箱都有额外开销
4. 类型转错还会运行时报错
这个点可以和[[万物之父和装箱与拆箱|装箱与拆箱]]一起理解。
----
# Capacity 和 Count
`Count`
当前实际有多少个元素
`Capacity`
内部数组当前能容纳多少个元素
```csharp
ArrayList list = new ArrayList();
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
list.Add("A");
list.Add("B");
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
```
理解:
`ArrayList` 内部会自动扩容
`Count` 是实际数量
`Capacity` 是容量,不一定等于数量
----
# 排序和反转
排序:
```csharp
ArrayList list = new ArrayList();
list.Add(3);
list.Add(1);
list.Add(2);
list.Sort();
```
反转:
```csharp
list.Reverse();
```
注意:
如果里面混放了不能互相比较的类型,`Sort()` 可能会出错
例如:
```csharp
list.Add(10);
list.Add("A");
list.Sort(); // 可能出错
```
所以虽然 `ArrayList` 可以存任意类型,但最好不要随便混放。
----
# 和数组的区别
| 对比点 | 数组 | ArrayList |
| ---- | --------- | -------------- |
| 长度 | 创建后固定 | 可以动态增删 |
| 类型 | 通常固定一种类型 | 内部按 `object` 存 |
| 访问 | 可以用下标 | 可以用下标 |
| 性能 | 类型明确,性能较好 | 可能有装箱拆箱 |
| 常用程度 | 基础常用 | 现在新项目较少用 |
----
# 常见错误
错误1:忘记引用命名空间
```csharp
ArrayList list = new ArrayList();
```
解决:
```csharp
using System.Collections;
```
----
错误2:取值时忘记强转
```csharp
ArrayList list = new ArrayList();
list.Add(10);
int num = list[0];
```
应该写:
```csharp
int num = (int)list[0];
```
----
错误3:混放类型后强转错误
```csharp
ArrayList list = new ArrayList();
list.Add("100");
int num = (int)list[0];
```
`list[0]` 实际是字符串,不是整数,会出错。
----
# 注意
1. `ArrayList``System.Collections` 命名空间中
2. `ArrayList` 是非泛型集合,内部按 `object` 存储
3. 它可以动态增删元素
4. 取出元素时通常需要强制类型转换
5. 值类型放入 `ArrayList` 会发生装箱
6. 不建议随意混放不同类型,容易产生转换错误
7. 新项目通常优先使用 `List<T>`,类型更安全
----
# 一句话记忆
==ArrayList 是会自动扩容的 object 数组,方便但不够类型安全。==
----
# 面试/复习时怎么说
可以这样答:
“ArrayList 是 `System.Collections` 命名空间下的非泛型集合,本质上可以理解为动态扩容的 `object` 数组。
它可以存储任意类型,但取值时通常需要强制类型转换,值类型放进去还会发生装箱。
所以它适合用来理解非泛型集合和装箱拆箱,新项目中一般更推荐使用泛型集合 `List<T>`。”
----
引用:
1. Microsoft Learn: [ArrayList Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-9.0)
2. Microsoft Learn: [System.Collections Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections?view=net-9.0)
#ArrayList
#集合
#进阶
+315
View File
@@ -0,0 +1,315 @@
# Hashtable
概念:
`Hashtable` 是 C# 提供的一个==非泛型键值对集合==
它通过键 `key` 来查找值 `value`
简单理解:
像一本字典
用“词”查“解释”
`key``value`
命名空间:
```csharp
using System.Collections;
```
----
# 通常用法
1. 保存键值对数据
2. 通过唯一键快速查找对应值
3. 学习哈希表这种数据结构
4. 理解 `Dictionary<TKey, TValue>` 之前的非泛型写法
实际开发中:
新代码更推荐使用 `Dictionary<TKey, TValue>`
`Hashtable` 主要用于学习非泛型键值对集合
----
# 语法
创建:
```csharp
Hashtable table = new Hashtable();
```
添加:
```csharp
table.Add(, );
```
通过键取值:
```csharp
object value = table[];
```
修改:
```csharp
table[] = ;
```
数量:
```csharp
table.Count
```
----
# 基础案例
```csharp
using System;
using System.Collections;
Hashtable table = new Hashtable();
table.Add("name", "小明");
table.Add("age", 18);
table.Add("isVip", true);
Console.WriteLine(table["name"]);
Console.WriteLine(table["age"]);
Console.WriteLine(table["isVip"]);
```
理解:
1. `"name"``"age"``"isVip"` 是键
2. `"小明"``18``true` 是值
3. 通过键可以找到对应的值
----
# Add 和索引器
使用 `Add` 添加:
```csharp
Hashtable table = new Hashtable();
table.Add("id", 1001);
table.Add("name", "小明");
```
使用索引器添加或修改:
```csharp
table["score"] = 90; // 如果 key 不存在,就是新增
table["score"] = 100; // 如果 key 已存在,就是修改
```
区别:
1. `Add` 遇到重复键会报错
2. `table[key] = value` 遇到重复键会覆盖旧值
----
# 增删查改
增加:
```csharp
table.Add("level", 10);
```
删除:
```csharp
table.Remove("level");
table.Clear();
```
查找键:
```csharp
bool hasName = table.ContainsKey("name");
```
查找值:
```csharp
bool hasValue = table.ContainsValue("小明");
```
修改:
```csharp
table["name"] = "小红";
```
取值:
```csharp
string name = (string)table["name"];
```
----
# 遍历
遍历键值对:
```csharp
Hashtable table = new Hashtable();
table.Add("name", "小明");
table.Add("age", 18);
foreach (DictionaryEntry item in table)
{
Console.WriteLine(item.Key + " : " + item.Value);
}
```
只遍历键:
```csharp
foreach (object key in table.Keys)
{
Console.WriteLine(key);
}
```
只遍历值:
```csharp
foreach (object value in table.Values)
{
Console.WriteLine(value);
}
```
注意:
`Hashtable` 的遍历顺序不保证等于添加顺序
----
# key 不能重复
错误写法:
```csharp
Hashtable table = new Hashtable();
table.Add("name", "小明");
table.Add("name", "小红");
```
原因:
`Hashtable` 中的 key 必须唯一
重复添加同一个 key 会报错
如果想修改旧值:
```csharp
table["name"] = "小红";
```
----
# key 不能是 null
错误写法:
```csharp
Hashtable table = new Hashtable();
table.Add(null, "值");
```
原因:
`Hashtable` 的 key 不能为 `null`
但是 value 可以是 `null`
```csharp
table.Add("data", null);
```
----
# 取值时要判断 key 是否存在
```csharp
Hashtable table = new Hashtable();
table.Add("name", "小明");
if (table.ContainsKey("name"))
{
string name = (string)table["name"];
Console.WriteLine(name);
}
```
为什么要判断:
1. key 不存在时,`table[key]` 可能拿到 `null`
2. value 本身也可能是 `null`
3.`ContainsKey` 可以区分“没有这个键”和“这个键的值是 null”
----
# 取值时要类型转换
```csharp
Hashtable table = new Hashtable();
table.Add("age", 18);
int age = (int)table["age"];
```
原因:
非泛型 `Hashtable` 的 key 和 value 都是按 `object` 处理
取出来后通常需要强制类型转换
如果类型转错:
```csharp
string age = (string)table["age"];
```
会出错,因为 `"age"` 对应的值实际是 `int`
----
# 和 ArrayList 的区别
| 对比点 | ArrayList | Hashtable |
|---|---|---|
| 存储方式 | 单个元素 | 键值对 |
| 访问方式 | 通过下标 | 通过 key |
| key 是否存在 | 没有 key | key 必须唯一 |
| 常见用途 | 普通列表 | 字典式查找 |
| 泛型替代 | `List<T>` | `Dictionary<TKey, TValue>` |
----
# 常见错误
错误1:重复 Add 同一个 key
```csharp
table.Add("id", 1);
table.Add("id", 2);
```
应该改成:
```csharp
table["id"] = 2;
```
----
错误2:以为 Hashtable 有添加顺序
实际:
`Hashtable` 是根据 key 的哈希来组织数据
遍历顺序不保证是添加顺序
----
错误3:取值后忘记强转
```csharp
int age = table["age"];
```
应该写:
```csharp
int age = (int)table["age"];
```
----
# 注意
1. `Hashtable``System.Collections` 命名空间中
2. `Hashtable` 是非泛型键值对集合
3. key 不能重复
4. key 不能是 `null`
5. value 可以是 `null`
6. `Add` 重复 key 会报错
7. `table[key] = value` 可以新增,也可以覆盖旧值
8. 遍历顺序不保证等于添加顺序
9. 新项目通常优先使用 `Dictionary<TKey, TValue>`
----
# 一句话记忆
==Hashtable 是用 key 找 value 的非泛型键值对集合。==
再压缩一点:
==ArrayList 靠下标找,Hashtable 靠 key 找。==
----
# 面试/复习时怎么说
可以这样答:
“Hashtable 是 `System.Collections` 命名空间下的非泛型键值对集合,通过唯一 key 查找 value。
它的 key 不能重复,也不能为 nullvalue 可以为 null。
因为 key 和 value 都按 object 处理,所以取值时通常需要强制类型转换。新项目中一般更推荐使用泛型集合 `Dictionary<TKey, TValue>`。”
----
引用:
1. Microsoft Learn: [Hashtable Class](https://learn.microsoft.com/dotnet/api/system.collections.hashtable?view=net-9.0)
2. Microsoft Learn: [Hashtable.Add Method](https://learn.microsoft.com/en-us/dotnet/api/system.collections.hashtable.add?view=net-9.0)
3. Microsoft Learn: [Hashtable.Item Property](https://learn.microsoft.com/en-us/dotnet/api/system.collections.hashtable.item?view=net-9.0)
4. Microsoft Learn: [System.Collections Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections?view=net-9.0)
#Hashtable
#哈希表
#集合
#进阶
+279
View File
@@ -0,0 +1,279 @@
# Queue
概念:
`Queue` 是 C# 提供的一个==非泛型队列集合==
队列的特点是:==先进先出==
英文:
`FIFO`First In First Out
简单理解:
像排队买东西
先排队的人,先被处理
命名空间:
```csharp
using System.Collections;
```
----
# 通常用法
1. 处理需要“按进入顺序依次处理”的数据
2. 保存等待处理的任务
3. 模拟排队、消息、请求等逻辑
4. 学习队列这种数据结构
实际开发中:
新代码更推荐使用 `Queue<T>`
这里的 `Queue` 是非泛型版本
----
# 语法
创建:
```csharp
Queue queue = new Queue();
```
入队:
```csharp
queue.Enqueue();
```
出队:
```csharp
object value = queue.Dequeue();
```
查看队头:
```csharp
object value = queue.Peek();
```
数量:
```csharp
queue.Count
```
----
# 基础案例
```csharp
using System;
using System.Collections;
Queue queue = new Queue();
queue.Enqueue("第一个任务");
queue.Enqueue("第二个任务");
queue.Enqueue("第三个任务");
Console.WriteLine(queue.Dequeue());
Console.WriteLine(queue.Dequeue());
Console.WriteLine(queue.Dequeue());
```
输出:
```text
第一个任务
第二个任务
第三个任务
```
理解:
1. `"第一个任务"` 最先进队列
2. `Dequeue()` 会先取出最早进入队列的元素
3. 这就是先进先出
----
# Enqueue、Dequeue、Peek
`Enqueue`
把元素添加到队尾
```csharp
queue.Enqueue("A");
```
`Dequeue`
取出并删除队头元素
```csharp
object item = queue.Dequeue();
```
`Peek`
查看队头元素,但不删除
```csharp
object item = queue.Peek();
```
案例:
```csharp
Queue queue = new Queue();
queue.Enqueue("A");
queue.Enqueue("B");
Console.WriteLine(queue.Peek()); // A
Console.WriteLine(queue.Count); // 2
Console.WriteLine(queue.Dequeue()); // A
Console.WriteLine(queue.Count); // 1
```
----
# 遍历
```csharp
Queue queue = new Queue();
queue.Enqueue("A");
queue.Enqueue("B");
queue.Enqueue("C");
foreach (object item in queue)
{
Console.WriteLine(item);
}
```
输出顺序一般是:
```text
A
B
C
```
理解:
遍历 `Queue` 时,通常按入队顺序从队头到队尾遍历
----
# 常用成员
```csharp
Queue queue = new Queue();
queue.Enqueue("A");
queue.Enqueue("B");
Console.WriteLine(queue.Count);
Console.WriteLine(queue.Contains("A"));
object first = queue.Peek();
object value = queue.Dequeue();
queue.Clear();
```
常用方法:
1. `Enqueue(object obj)`:入队
2. `Dequeue()`:出队并删除
3. `Peek()`:查看队头但不删除
4. `Contains(object obj)`:判断是否包含
5. `Clear()`:清空
6. `ToArray()`:转成数组
----
# 空队列不能 Dequeue 或 Peek
错误写法:
```csharp
Queue queue = new Queue();
queue.Dequeue();
```
原因:
队列里没有元素,不能取队头
更稳的写法:
```csharp
if (queue.Count > 0)
{
object value = queue.Dequeue();
}
```
`Peek()` 也一样,使用前最好先判断 `Count`
----
# 取值时要类型转换
```csharp
Queue queue = new Queue();
queue.Enqueue(100);
int num = (int)queue.Dequeue();
```
原因:
非泛型 `Queue` 中的元素按 `object` 存储
取出来也是 `object`
值类型入队和出队时,也会涉及装箱拆箱:
```csharp
queue.Enqueue(100); // 装箱
int num = (int)queue.Dequeue(); // 拆箱
```
----
# 适合场景:任务队列
```csharp
Queue taskQueue = new Queue();
taskQueue.Enqueue("加载配置");
taskQueue.Enqueue("读取存档");
taskQueue.Enqueue("进入场景");
while (taskQueue.Count > 0)
{
string task = (string)taskQueue.Dequeue();
Console.WriteLine("处理:" + task);
}
```
理解:
任务按照加入顺序依次处理
这种情况适合用队列
----
# Queue 和 Stack 的区别
| 对比点 | Queue | Stack |
|---|---|---|
| 规则 | 先进先出 | 后进先出 |
| 英文 | FIFO | LIFO |
| 添加 | `Enqueue` | `Push` |
| 取出 | `Dequeue` | `Pop` |
| 查看 | `Peek` 查看队头 | `Peek` 查看栈顶 |
| 适合场景 | 排队处理任务 | 撤销、回退 |
一句话:
排队用 `Queue`
回退用 `Stack`
----
# 注意
1. `Queue``System.Collections` 命名空间中
2. `Queue` 是非泛型集合,元素按 `object` 存储
3. 队列的规则是先进先出
4. `Enqueue` 是入队
5. `Dequeue` 是出队并删除队头
6. `Peek` 是查看队头但不删除
7. 空队列调用 `Dequeue()``Peek()` 会出错
8. 新项目通常优先使用 `Queue<T>`
----
# 一句话记忆
==Queue 是先进先出的集合,先进去的先出来。==
----
# 面试/复习时怎么说
可以这样答:
“Queue 是队列结构,特点是先进先出,也就是 FIFO。
常用方法有 `Enqueue``Dequeue``Peek`,分别表示入队、出队、查看队头。
非泛型 Queue 存的是 object,取值时可能需要强制类型转换,新项目中更推荐使用 `Queue<T>`。”
----
引用:
1. Microsoft Learn: [Queue Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.queue?view=net-9.0)
2. Microsoft Learn: [Queue.Enqueue Method](https://learn.microsoft.com/en-us/dotnet/api/system.collections.queue.enqueue?view=net-9.0)
3. Microsoft Learn: [System.Collections Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections?view=net-9.0)
#Queue
#队列
#集合
#进阶
+273
View File
@@ -0,0 +1,273 @@
# Stack
概念:
`Stack` 是 C# 提供的一个==非泛型栈集合==
栈的特点是:==先进后出,后进先出==
英文:
`LIFO`Last In First Out
简单理解:
像一摞盘子
最后放上去的,最先被拿走
命名空间:
```csharp
using System.Collections;
```
----
# 通常用法
1. 处理需要“后进先出”的数据
2. 临时保存操作记录
3. 实现撤销、回退、调用顺序等逻辑
4. 学习栈这种数据结构
实际开发中:
新代码更推荐使用 `Stack<T>`
这里的 `Stack` 是非泛型版本,主要用于理解集合和数据结构
----
# 语法
创建:
```csharp
Stack stack = new Stack();
```
入栈:
```csharp
stack.Push();
```
出栈:
```csharp
object value = stack.Pop();
```
查看栈顶:
```csharp
object value = stack.Peek();
```
数量:
```csharp
stack.Count
```
----
# 基础案例
```csharp
using System;
using System.Collections;
Stack stack = new Stack();
stack.Push("第一步");
stack.Push("第二步");
stack.Push("第三步");
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
```
输出:
```text
第三步
第二步
第一步
```
理解:
1. `"第一步"` 最先进栈
2. `"第三步"` 最后进栈
3. `Pop()` 会先取出最后进去的元素
----
# Push、Pop、Peek
`Push`
把元素放到栈顶
```csharp
stack.Push("A");
```
`Pop`
取出并删除栈顶元素
```csharp
object item = stack.Pop();
```
`Peek`
只查看栈顶元素,不删除
```csharp
object item = stack.Peek();
```
案例:
```csharp
Stack stack = new Stack();
stack.Push("A");
stack.Push("B");
Console.WriteLine(stack.Peek()); // B
Console.WriteLine(stack.Count); // 2
Console.WriteLine(stack.Pop()); // B
Console.WriteLine(stack.Count); // 1
```
----
# 遍历
```csharp
Stack stack = new Stack();
stack.Push("A");
stack.Push("B");
stack.Push("C");
foreach (object item in stack)
{
Console.WriteLine(item);
}
```
输出顺序一般是:
```text
C
B
A
```
理解:
遍历 `Stack` 时,也是从栈顶到栈底
----
# 常用成员
```csharp
Stack stack = new Stack();
stack.Push("A");
stack.Push("B");
Console.WriteLine(stack.Count);
Console.WriteLine(stack.Contains("A"));
object top = stack.Peek();
object value = stack.Pop();
stack.Clear();
```
常用方法:
1. `Push(object obj)`:入栈
2. `Pop()`:出栈并删除
3. `Peek()`:查看栈顶但不删除
4. `Contains(object obj)`:判断是否包含
5. `Clear()`:清空
6. `ToArray()`:转成数组
----
# 空栈不能 Pop 或 Peek
错误写法:
```csharp
Stack stack = new Stack();
stack.Pop();
```
原因:
栈里没有元素,不能取栈顶
更稳的写法:
```csharp
if (stack.Count > 0)
{
object value = stack.Pop();
}
```
`Peek()` 也一样,使用前最好先判断 `Count`
----
# 取值时要类型转换
```csharp
Stack stack = new Stack();
stack.Push(100);
int num = (int)stack.Pop();
```
原因:
非泛型 `Stack` 中的元素按 `object` 存储
取出来也是 `object`
如果值类型放进去,会涉及装箱和拆箱:
```csharp
stack.Push(100); // 装箱
int num = (int)stack.Pop(); // 拆箱
```
----
# 适合场景:撤销操作
```csharp
Stack undoStack = new Stack();
undoStack.Push("创建角色");
undoStack.Push("移动角色");
undoStack.Push("修改颜色");
if (undoStack.Count > 0)
{
string lastAction = (string)undoStack.Pop();
Console.WriteLine("撤销:" + lastAction);
}
```
理解:
最后做的操作,通常最先撤销
所以适合用栈
----
# Stack 和 ArrayList 的区别
| 对比点 | ArrayList | Stack |
|---|---|---|
| 访问方式 | 按下标访问 | 只关心栈顶 |
| 数据顺序 | 按添加顺序保存 | 后进先出 |
| 添加方法 | `Add` | `Push` |
| 取出方法 | `list[index]` | `Pop` / `Peek` |
| 适合场景 | 普通列表 | 回退、撤销、临时反向处理 |
----
# 注意
1. `Stack``System.Collections` 命名空间中
2. `Stack` 是非泛型集合,元素按 `object` 存储
3. 栈的规则是后进先出
4. `Push` 是入栈
5. `Pop` 是取出并删除栈顶
6. `Peek` 是查看栈顶但不删除
7. 空栈调用 `Pop()``Peek()` 会出错
8. 新项目通常优先使用 `Stack<T>`
----
# 一句话记忆
==Stack 是后进先出的集合,最后放进去的最先出来。==
----
# 面试/复习时怎么说
可以这样答:
“Stack 是栈结构,特点是后进先出,也就是 LIFO。
常用方法有 `Push``Pop``Peek`,分别表示入栈、出栈、查看栈顶。
非泛型 Stack 存的是 object,取值时可能需要强制类型转换,新项目中更推荐使用 `Stack<T>`。”
----
引用:
1. Microsoft Learn: [Stack Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.stack?view=net-9.0)
2. Microsoft Learn: [System.Collections Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections?view=net-9.0)
#Stack
#栈
#集合
#进阶
+380
View File
@@ -0,0 +1,380 @@
# 泛型
概念:
泛型就是把==类型==参数化,让一份代码可以适配多种数据类型。
可以理解为:先把类型空出来,等使用时再指定具体类型。
例如:
```csharp
List<int> nums = new List<int>();
List<string> names = new List<string>();
```
其中 `int``string` 就是传入泛型的具体类型。
----
# 为什么需要泛型
泛型的主要作用:
1. 提高代码复用性
2. 保证类型安全
3. 减少强制类型转换
4. 避免不必要的装箱和拆箱
对比 `ArrayList`
```csharp
ArrayList list = new ArrayList();
list.Add(1);
list.Add("hello");
int num = (int)list[0];
```
`ArrayList` 内部存的是 `object`,取值时经常需要强制转换。
使用泛型集合:
```csharp
List<int> list = new List<int>();
list.Add(1);
int num = list[0];
```
`List<int>` 只能存 `int`,编译器会提前帮我们检查类型错误。
----
# 常见泛型形式
泛型可以用于:
1. 泛型类
2. 泛型接口
3. 泛型方法
4. 泛型委托
5. 泛型集合
常见泛型集合:
```csharp
List<T>
Dictionary<TKey, TValue>
Queue<T>
Stack<T>
HashSet<T>
```
`T``TKey``TValue` 都是泛型类型参数名。
----
# 语法
泛型类:
```csharp
class <T>
{
}
```
泛型接口:
```csharp
interface <T>
{
}
```
泛型方法:
```csharp
<T>(T )
{
}
```
多个泛型类型参数:
```csharp
class <T1, T2>
{
}
```
----
# 泛型类
泛型类适合用来保存或处理某种不确定类型的数据。
```csharp
class Box<T>
{
public T Value { get; set; }
public Box(T value)
{
Value = value;
}
public T GetValue()
{
return Value;
}
}
```
使用:
```csharp
Box<int> intBox = new Box<int>(10);
Console.WriteLine(intBox.GetValue());
Box<string> stringBox = new Box<string>("hello");
Console.WriteLine(stringBox.GetValue());
```
理解:
1. `Box<T>` 定义时不知道 `T` 是什么类型
2. `Box<int>` 使用时把 `T` 替换成 `int`
3. `Box<string>` 使用时把 `T` 替换成 `string`
----
# 泛型接口
泛型接口常用于规定一类对象必须提供某种泛型能力。
```csharp
interface IRepository<T>
{
void Add(T item);
T Get(int index);
}
```
实现:
```csharp
class MemoryRepository<T> : IRepository<T>
{
private List<T> items = new List<T>();
public void Add(T item)
{
items.Add(item);
}
public T Get(int index)
{
return items[index];
}
}
```
使用:
```csharp
IRepository<string> repository = new MemoryRepository<string>();
repository.Add("小明");
Console.WriteLine(repository.Get(0));
```
----
# 泛型方法
泛型方法适合处理“逻辑一样,但参数类型不同”的情况。
```csharp
static void Print<T>(T value)
{
Console.WriteLine(value);
}
```
使用:
```csharp
Print<int>(100);
Print<string>("hello");
Print<bool>(true);
```
很多时候,编译器可以自动推断泛型类型:
```csharp
Print(100);
Print("hello");
Print(true);
```
----
# 泛型约束
默认情况下,泛型类型 `T` 可以是任意类型。
如果希望限制 `T` 必须满足某些条件,可以使用 `where` 约束。
语法:
```csharp
class <T> where T :
{
}
```
常见约束:
| 约束 | 含义 |
| --- | --- |
| `where T : class` | `T` 必须是引用类型 |
| `where T : struct` | `T` 必须是值类型 |
| `where T : new()` | `T` 必须有无参构造函数 |
| `where T : 父类名` | `T` 必须继承某个父类 |
| `where T : 接口名` | `T` 必须实现某个接口 |
案例:
```csharp
class Factory<T> where T : new()
{
public T Create()
{
return new T();
}
}
```
因为加了 `where T : new()`,所以可以在类中使用 `new T()`
----
# 多泛型参数
一个类或方法可以同时使用多个泛型参数。
```csharp
class Pair<TKey, TValue>
{
public TKey Key { get; set; }
public TValue Value { get; set; }
public Pair(TKey key, TValue value)
{
Key = key;
Value = value;
}
}
```
使用:
```csharp
Pair<int, string> pair = new Pair<int, string>(1, "小明");
Console.WriteLine(pair.Key);
Console.WriteLine(pair.Value);
```
----
# default 关键字
泛型中有时不知道 `T` 的默认值是什么,可以使用 `default`
```csharp
static T GetDefault<T>()
{
return default(T);
}
```
常见默认值:
| 类型 | 默认值 |
| --- | --- |
| `int` | `0` |
| `bool` | `false` |
| 引用类型 | `null` |
| 结构体 | 所有字段都是默认值 |
新语法也可以写成:
```csharp
return default;
```
----
# 完整案例
```csharp
using System;
using System.Collections.Generic;
namespace GenericsDemo
{
internal class Program
{
static void Main(string[] args)
{
DataStore<int> intStore = new DataStore<int>();
intStore.Add(10);
intStore.Add(20);
Console.WriteLine(intStore.Get(0));
Console.WriteLine(intStore.Get(1));
DataStore<string> stringStore = new DataStore<string>();
stringStore.Add("hello");
stringStore.Add("world");
Console.WriteLine(stringStore.Get(0));
Console.WriteLine(stringStore.Get(1));
Print(123);
Print("泛型方法");
}
static void Print<T>(T value)
{
Console.WriteLine(value);
}
}
interface IDataStore<T>
{
void Add(T value);
T Get(int index);
}
class DataStore<T> : IDataStore<T>
{
private List<T> values = new List<T>();
public void Add(T value)
{
values.Add(value);
}
public T Get(int index)
{
return values[index];
}
}
}
```
理解:
1. `DataStore<T>` 是泛型类
2. `IDataStore<T>` 是泛型接口
3. `Print<T>` 是泛型方法
4. `DataStore<int>` 只能保存整数
5. `DataStore<string>` 只能保存字符串
----
# 注意
1. 泛型类型参数常用 `T` 表示,但不是固定写法
2. 多个泛型参数常写成 `TKey``TValue``TItem`
3. `List<T>``ArrayList` 更常用,也更安全
4. 泛型在编译期就能检查类型错误
5. 使用泛型可以减少 `object` 带来的强制转换
6. 如果需要在泛型中 `new T()`,必须加 `where T : new()` 约束
7. 泛型不是“任何类型都乱放”,而是“使用时确定一种类型”
----
# 小结
一句话:
泛型就是==把类型当作参数传进去==。
记忆:
普通参数控制“值”。
泛型参数控制“类型”。
最常见写法:
```csharp
List<int> nums = new List<int>();
Dictionary<string, int> scores = new Dictionary<string, int>();
```