日常更新一些笔记
This commit is contained in:
@@ -47,4 +47,5 @@ done today
|
|||||||
sort by done reverse
|
sort by done reverse
|
||||||
```
|
```
|
||||||
|
|
||||||
* 2026-06-20 - 2
|
* 2026-06-20 - 2
|
||||||
|
* 2026-06-22 - 2
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
# 日常 List
|
# 日常 List
|
||||||
> 这里放每天会自动刷新的小任务。完成后 Tasks 会按 `🔁 every day` 生成下一次。
|
> 这里放每天会自动刷新的小任务。完成后 Tasks 会按 `🔁 every day` 生成下一次。
|
||||||
|
|
||||||
- [ ] eNSP #daily 🔺 🔁 every day 📅 2026-06-22
|
- [ ] eNSP #daily 🔺 🔁 every day 📅 2026-06-23
|
||||||
|
- [x] eNSP #daily 🔺 🔁 every day 📅 2026-06-22 ✅ 2026-06-22
|
||||||
- [x] eNSP #daily 🔺 🔁 every day 📅 2026-06-21 ✅ 2026-06-21
|
- [x] eNSP #daily 🔺 🔁 every day 📅 2026-06-21 ✅ 2026-06-21
|
||||||
- [ ] Linux #daily 🔺 🔁 every day 📅 2026-06-22
|
- [ ] Linux #daily 🔺 🔁 every day 📅 2026-06-23
|
||||||
|
- [x] Linux #daily 🔺 🔁 every day 📅 2026-06-22 ✅ 2026-06-22
|
||||||
- [x] Linux #daily 🔺 🔁 every day 📅 2026-06-21 ✅ 2026-06-21
|
- [x] Linux #daily 🔺 🔁 every day 📅 2026-06-21 ✅ 2026-06-21
|
||||||
- [ ] C# #daily ⏫ 🔁 every day 📅 2026-06-21
|
- [ ] C# #daily ⏫ 🔁 every day 📅 2026-06-23
|
||||||
|
- [x] C# #daily ⏫ 🔁 every day 📅 2026-06-22 ✅ 2026-06-22
|
||||||
|
- [x] C# #daily ⏫ 🔁 every day 📅 2026-06-21 ✅ 2026-06-22
|
||||||
- [ ] C++ 或 UE #weekly ⏬ 🔁 every week
|
- [ ] C++ 或 UE #weekly ⏬ 🔁 every week
|
||||||
- [ ] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-21
|
- [ ] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-22
|
||||||
- [ ] 学习吉他 🔼 🔁 every day 📅 2026-06-21
|
- [x] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-21 ✅ 2026-06-22
|
||||||
|
- [ ] 学习吉他 🔼 🔁 every day 📅 2026-06-22
|
||||||
|
- [x] 学习吉他 🔼 🔁 every day 📅 2026-06-21 ✅ 2026-06-22
|
||||||
- [x] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-20 ✅ 2026-06-20
|
- [x] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-20 ✅ 2026-06-20
|
||||||
|
- [ ] 主页打卡⏬ 🔁 every day 📅 2026-06-21
|
||||||
|
|
||||||
## 显示今日的样例:
|
## 显示今日的样例:
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ sort by priority
|
|||||||
|
|
||||||
> ==记得把今日任务详细的规划在时间线上↓==
|
> ==记得把今日任务详细的规划在时间线上↓==
|
||||||
|
|
||||||
- [ ] 09:00 - 09:30
|
- [ ] 14:40 - 15:40 eNSP
|
||||||
|
|
||||||
## 复盘
|
## 复盘
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,409 @@
|
|||||||
|
# Dictionary
|
||||||
|
概念:
|
||||||
|
`Dictionary<TKey, TValue>` 是 C# 提供的==泛型键值对集合==
|
||||||
|
它通过键 `key` 来快速查找值 `value`
|
||||||
|
|
||||||
|
简单理解:
|
||||||
|
像一本字典
|
||||||
|
用“词”查“解释”
|
||||||
|
用 `key` 查 `value`
|
||||||
|
|
||||||
|
命名空间:
|
||||||
|
```csharp
|
||||||
|
using System.Collections.Generic;
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 通常用法
|
||||||
|
1. 保存键值对数据
|
||||||
|
2. 通过唯一键快速查找对应值
|
||||||
|
3. 根据 `id`、名字、编号等信息管理对象
|
||||||
|
4. 替代早期非泛型键值对集合 `Hashtable`
|
||||||
|
|
||||||
|
实际开发中:
|
||||||
|
`Dictionary<TKey, TValue>` 非常常用
|
||||||
|
只要需求是“通过 key 快速找到 value”
|
||||||
|
通常都可以先考虑它
|
||||||
|
|
||||||
|
----
|
||||||
|
# 语法
|
||||||
|
创建:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
```
|
||||||
|
|
||||||
|
添加:
|
||||||
|
```csharp
|
||||||
|
dict.Add("语文", 90);
|
||||||
|
```
|
||||||
|
|
||||||
|
通过键取值:
|
||||||
|
```csharp
|
||||||
|
int score = dict["语文"];
|
||||||
|
```
|
||||||
|
|
||||||
|
修改:
|
||||||
|
```csharp
|
||||||
|
dict["语文"] = 95;
|
||||||
|
```
|
||||||
|
|
||||||
|
数量:
|
||||||
|
```csharp
|
||||||
|
dict.Count
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 基础案例
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
Dictionary<string, int> scores = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
scores.Add("语文", 90);
|
||||||
|
scores.Add("数学", 95);
|
||||||
|
scores.Add("英语", 88);
|
||||||
|
|
||||||
|
Console.WriteLine(scores["语文"]);
|
||||||
|
Console.WriteLine(scores["数学"]);
|
||||||
|
Console.WriteLine(scores["英语"]);
|
||||||
|
Console.WriteLine(scores.Count);
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
```text
|
||||||
|
90
|
||||||
|
95
|
||||||
|
88
|
||||||
|
3
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
1. `"语文"`、`"数学"`、`"英语"` 是键
|
||||||
|
2. `90`、`95`、`88` 是值
|
||||||
|
3. 通过 key 能快速找到对应 value
|
||||||
|
|
||||||
|
----
|
||||||
|
# Add 和索引器
|
||||||
|
使用 `Add` 添加:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
dict.Add("hp", 100);
|
||||||
|
dict.Add("mp", 50);
|
||||||
|
```
|
||||||
|
|
||||||
|
使用索引器新增或修改:
|
||||||
|
```csharp
|
||||||
|
dict["atk"] = 20; // key 不存在时,新增
|
||||||
|
dict["atk"] = 25; // key 已存在时,修改
|
||||||
|
```
|
||||||
|
|
||||||
|
区别:
|
||||||
|
1. `Add` 遇到重复 key 会报错
|
||||||
|
2. `dict[key] = value` 遇到重复 key 会覆盖旧值
|
||||||
|
|
||||||
|
----
|
||||||
|
# 增删查改
|
||||||
|
增加:
|
||||||
|
```csharp
|
||||||
|
dict.Add("def", 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
删除:
|
||||||
|
```csharp
|
||||||
|
dict.Remove("def");
|
||||||
|
dict.Clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
查找键:
|
||||||
|
```csharp
|
||||||
|
bool hasHp = dict.ContainsKey("hp");
|
||||||
|
```
|
||||||
|
|
||||||
|
查找值:
|
||||||
|
```csharp
|
||||||
|
bool hasValue = dict.ContainsValue(100);
|
||||||
|
```
|
||||||
|
|
||||||
|
修改:
|
||||||
|
```csharp
|
||||||
|
dict["hp"] = 120;
|
||||||
|
```
|
||||||
|
|
||||||
|
取值:
|
||||||
|
```csharp
|
||||||
|
int hp = dict["hp"];
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# TryGetValue 很常用
|
||||||
|
直接取值:
|
||||||
|
```csharp
|
||||||
|
int hp = dict["hp"];
|
||||||
|
```
|
||||||
|
|
||||||
|
问题:
|
||||||
|
如果 key 不存在
|
||||||
|
会报错
|
||||||
|
|
||||||
|
更稳的写法:
|
||||||
|
```csharp
|
||||||
|
if (dict.TryGetValue("hp", out int hp))
|
||||||
|
{
|
||||||
|
Console.WriteLine(hp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("没有这个键");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
1. `TryGetValue` 会返回 `true` 或 `false`
|
||||||
|
2. 如果找到 key,就把对应值放进 `out` 变量
|
||||||
|
3. 如果项目里经常“先查再取”,`TryGetValue` 很实用
|
||||||
|
|
||||||
|
----
|
||||||
|
# 遍历
|
||||||
|
遍历键值对:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
dict.Add("语文", 90);
|
||||||
|
dict.Add("数学", 95);
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, int> item in dict)
|
||||||
|
{
|
||||||
|
Console.WriteLine(item.Key + " : " + item.Value);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
只遍历键:
|
||||||
|
```csharp
|
||||||
|
foreach (string key in dict.Keys)
|
||||||
|
{
|
||||||
|
Console.WriteLine(key);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
只遍历值:
|
||||||
|
```csharp
|
||||||
|
foreach (int value in dict.Values)
|
||||||
|
{
|
||||||
|
Console.WriteLine(value);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:
|
||||||
|
复习时不要把 `Dictionary` 当成“按下标顺序排列的列表”
|
||||||
|
它的核心用途是通过 key 查 value
|
||||||
|
|
||||||
|
----
|
||||||
|
# key 不能重复
|
||||||
|
错误写法:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
dict.Add("hp", 100);
|
||||||
|
dict.Add("hp", 200);
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
`Dictionary` 中的 key 必须唯一
|
||||||
|
重复添加同一个 key 会报错
|
||||||
|
|
||||||
|
如果想修改旧值:
|
||||||
|
```csharp
|
||||||
|
dict["hp"] = 200;
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# key 一般不能为 null
|
||||||
|
错误写法:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
// dict.Add(null, 100);
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
`Dictionary` 的 key 不能为 `null`
|
||||||
|
|
||||||
|
补充:
|
||||||
|
如果 `TValue` 是引用类型或可空类型
|
||||||
|
value 可以是 `null`
|
||||||
|
|
||||||
|
示例:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||||
|
dict["name"] = null;
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 和 Hashtable 的区别
|
||||||
|
`Hashtable`:
|
||||||
|
非泛型
|
||||||
|
key 和 value 都按 `object` 处理
|
||||||
|
取值时常常需要强制类型转换
|
||||||
|
|
||||||
|
`Dictionary<TKey, TValue>`:
|
||||||
|
泛型
|
||||||
|
key 和 value 类型明确
|
||||||
|
编译期就能检查类型错误
|
||||||
|
使用起来更安全,更常见
|
||||||
|
|
||||||
|
对比表:
|
||||||
|
|
||||||
|
| 对比点 | `Hashtable` | `Dictionary<TKey, TValue>` |
|
||||||
|
|---|---|---|
|
||||||
|
| 类型 | 非泛型 | 泛型 |
|
||||||
|
| 键和值 | `object` | 指定类型 |
|
||||||
|
| 类型安全 | 较弱 | 更强 |
|
||||||
|
| 取值 | 常要强转 | 一般不用强转 |
|
||||||
|
| 推荐程度 | 主要用于学习旧集合 | 实际开发常用 |
|
||||||
|
|
||||||
|
----
|
||||||
|
# 和 List 的区别
|
||||||
|
| 对比点 | `List<T>` | `Dictionary<TKey, TValue>` |
|
||||||
|
|---|---|---|
|
||||||
|
| 存储方式 | 单个元素 | 键值对 |
|
||||||
|
| 查找方式 | 常用下标 | 常用 key |
|
||||||
|
| key | 没有 | 必须唯一 |
|
||||||
|
| 适合场景 | 一组同类型数据 | 根据唯一键查数据 |
|
||||||
|
|
||||||
|
一句话:
|
||||||
|
`List<T>` 更像“列表”
|
||||||
|
`Dictionary<TKey, TValue>` 更像“字典”
|
||||||
|
|
||||||
|
----
|
||||||
|
# 基础案例:角色表
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
class Player
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public int Level;
|
||||||
|
|
||||||
|
public Player(string name, int level)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Level = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Dictionary<int, Player> players = new Dictionary<int, Player>();
|
||||||
|
|
||||||
|
players.Add(1001, new Player("小明", 10));
|
||||||
|
players.Add(1002, new Player("小红", 12));
|
||||||
|
players.Add(1003, new Player("小刚", 8));
|
||||||
|
|
||||||
|
if (players.TryGetValue(1002, out Player player))
|
||||||
|
{
|
||||||
|
Console.WriteLine(player.Name + " " + player.Level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
```text
|
||||||
|
小红 12
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
1. `1001`、`1002`、`1003` 是玩家编号
|
||||||
|
2. 通过编号可以快速找到对应玩家对象
|
||||||
|
3. 这种“编号 -> 对象”的场景非常适合用 `Dictionary`
|
||||||
|
|
||||||
|
----
|
||||||
|
# 常用成员
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
dict.Add("A", 1);
|
||||||
|
dict["B"] = 2;
|
||||||
|
|
||||||
|
Console.WriteLine(dict.Count);
|
||||||
|
Console.WriteLine(dict.ContainsKey("A"));
|
||||||
|
Console.WriteLine(dict.ContainsValue(2));
|
||||||
|
|
||||||
|
dict.Remove("A");
|
||||||
|
dict.Clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
常用方法 / 成员:
|
||||||
|
1. `Add(TKey key, TValue value)`:添加键值对
|
||||||
|
2. `Remove(TKey key)`:删除指定 key
|
||||||
|
3. `Clear()`:清空
|
||||||
|
4. `ContainsKey(TKey key)`:判断 key 是否存在
|
||||||
|
5. `ContainsValue(TValue value)`:判断 value 是否存在
|
||||||
|
6. `TryGetValue(TKey key, out TValue value)`:安全取值
|
||||||
|
7. `Keys`:获取所有键
|
||||||
|
8. `Values`:获取所有值
|
||||||
|
9. `Count`:键值对数量
|
||||||
|
10. `dict[key]`:通过 key 取值或赋值
|
||||||
|
|
||||||
|
----
|
||||||
|
# 注意
|
||||||
|
1. `Dictionary<TKey, TValue>` 在 `System.Collections.Generic` 命名空间中
|
||||||
|
2. 它是泛型键值对集合
|
||||||
|
3. key 必须唯一
|
||||||
|
4. `Add` 遇到重复 key 会报错
|
||||||
|
5. `dict[key] = value` 可以新增,也可以覆盖旧值
|
||||||
|
6. 用不存在的 key 直接取值可能报错
|
||||||
|
7. 更稳的取值方式通常是 `TryGetValue`
|
||||||
|
8. 它最核心的优势是“通过 key 快速找 value”
|
||||||
|
|
||||||
|
----
|
||||||
|
# 易错点
|
||||||
|
1. 不要把 `Dictionary` 当数组或 `List<T>` 来用
|
||||||
|
2. 不要默认 key 一定存在
|
||||||
|
3. `ContainsKey` 判断的是键,不是值
|
||||||
|
4. `ContainsValue` 可以用,但实际开发里更常关心 key
|
||||||
|
5. 重复 `Add` 同一个 key 会报错
|
||||||
|
|
||||||
|
错误示例:
|
||||||
|
```csharp
|
||||||
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
||||||
|
dict.Add("hp", 100);
|
||||||
|
|
||||||
|
// Console.WriteLine(dict["mp"]);
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
`"mp"` 这个 key 不存在
|
||||||
|
直接取值可能报错
|
||||||
|
|
||||||
|
----
|
||||||
|
# 一句话记忆
|
||||||
|
==Dictionary<TKey, TValue> 是通过唯一 key 快速查找 value 的泛型键值对集合。==
|
||||||
|
|
||||||
|
再压缩一点:
|
||||||
|
==List 用下标找,Dictionary 用 key 找。==
|
||||||
|
|
||||||
|
----
|
||||||
|
# 面试/复习时怎么说
|
||||||
|
可以这样答:
|
||||||
|
|
||||||
|
“`Dictionary<TKey, TValue>` 是 C# 中非常常用的泛型键值对集合,适合通过唯一 key 快速查找 value。
|
||||||
|
它的 key 不能重复,常用操作有 `Add`、`Remove`、`ContainsKey` 和 `TryGetValue`。
|
||||||
|
相比 `Hashtable`,`Dictionary` 类型更安全,不需要频繁强制类型转换,所以实际开发中更常用。”
|
||||||
|
|
||||||
|
----
|
||||||
|
引用:
|
||||||
|
1. Microsoft Learn: [Dictionary<TKey,TValue> Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-9.0)
|
||||||
|
2. Microsoft Learn: [Dictionary<TKey,TValue>.TryGetValue Method](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetvalue?view=net-9.0)
|
||||||
|
3. Microsoft Learn: [System.Collections.Generic Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic?view=net-9.0)
|
||||||
|
|
||||||
|
#Dictionary
|
||||||
|
#字典
|
||||||
|
#泛型集合
|
||||||
|
#集合
|
||||||
|
#进阶
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
# List
|
||||||
|
概念:
|
||||||
|
`List<T>` 是 C# 提供的==泛型集合类==
|
||||||
|
本质上可以理解为:==一个长度可变的泛型数组==
|
||||||
|
|
||||||
|
它可以:
|
||||||
|
1. 按下标访问元素
|
||||||
|
2. 自动扩容
|
||||||
|
3. 方便地增删查改
|
||||||
|
4. 保证集合中元素类型统一
|
||||||
|
|
||||||
|
命名空间:
|
||||||
|
```csharp
|
||||||
|
using System.Collections.Generic;
|
||||||
|
```
|
||||||
|
|
||||||
|
简单理解:
|
||||||
|
数组长度固定
|
||||||
|
`List<T>` 长度可以动态变化
|
||||||
|
而且比 `ArrayList` 更安全,因为它有明确类型
|
||||||
|
|
||||||
|
----
|
||||||
|
# 通常用法
|
||||||
|
1. 存储数量不固定的一组同类型数据
|
||||||
|
2. 需要频繁添加、删除、遍历元素
|
||||||
|
3. 作为项目里最常见的基础集合之一
|
||||||
|
4. 替代早期非泛型集合 `ArrayList`
|
||||||
|
|
||||||
|
实际开发中:
|
||||||
|
新项目里 `List<T>` 的使用频率非常高
|
||||||
|
很多时候它就是“默认列表容器”
|
||||||
|
|
||||||
|
----
|
||||||
|
# 语法
|
||||||
|
创建:
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>();
|
||||||
|
```
|
||||||
|
|
||||||
|
创建并赋初值:
|
||||||
|
```csharp
|
||||||
|
List<string> names = new List<string>() { "小明", "小红", "小刚" };
|
||||||
|
```
|
||||||
|
|
||||||
|
添加:
|
||||||
|
```csharp
|
||||||
|
list.Add(10);
|
||||||
|
```
|
||||||
|
|
||||||
|
访问:
|
||||||
|
```csharp
|
||||||
|
list[0]
|
||||||
|
```
|
||||||
|
|
||||||
|
数量:
|
||||||
|
```csharp
|
||||||
|
list.Count
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 基础案例
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
List<int> list = new List<int>();
|
||||||
|
|
||||||
|
list.Add(10);
|
||||||
|
list.Add(20);
|
||||||
|
list.Add(30);
|
||||||
|
|
||||||
|
Console.WriteLine(list[0]);
|
||||||
|
Console.WriteLine(list[1]);
|
||||||
|
Console.WriteLine(list[2]);
|
||||||
|
Console.WriteLine(list.Count);
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
```text
|
||||||
|
10
|
||||||
|
20
|
||||||
|
30
|
||||||
|
3
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
1. `List<int>` 里只能放 `int`
|
||||||
|
2. 可以像数组一样通过下标访问
|
||||||
|
3. `Count` 表示当前实际元素个数
|
||||||
|
|
||||||
|
----
|
||||||
|
# 和 ArrayList 的区别
|
||||||
|
`ArrayList`:
|
||||||
|
内部按 `object` 存储
|
||||||
|
可以混合存很多类型
|
||||||
|
取值时常常需要强制类型转换
|
||||||
|
值类型放进去会发生装箱拆箱
|
||||||
|
|
||||||
|
`List<T>`:
|
||||||
|
内部是泛型集合
|
||||||
|
类型更明确
|
||||||
|
编译期就能检查类型错误
|
||||||
|
通常不会像 `ArrayList` 那样频繁发生装箱拆箱
|
||||||
|
|
||||||
|
对比表:
|
||||||
|
|
||||||
|
| 对比点 | `ArrayList` | `List<T>` |
|
||||||
|
|---|---|---|
|
||||||
|
| 类型 | 非泛型 | 泛型 |
|
||||||
|
| 存储 | `object` | 指定类型 `T` |
|
||||||
|
| 类型安全 | 较弱 | 更强 |
|
||||||
|
| 取值 | 常要强转 | 一般不用强转 |
|
||||||
|
| 性能 | 值类型可能有装箱拆箱 | 通常更好 |
|
||||||
|
| 推荐程度 | 主要用于学习旧集合 | 实际开发常用 |
|
||||||
|
|
||||||
|
----
|
||||||
|
# 增删查改
|
||||||
|
增加:
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>();
|
||||||
|
|
||||||
|
list.Add(1);
|
||||||
|
list.Add(2);
|
||||||
|
list.Add(3);
|
||||||
|
|
||||||
|
list.Insert(1, 99);
|
||||||
|
list.AddRange(new List<int> { 7, 8, 9 });
|
||||||
|
```
|
||||||
|
|
||||||
|
删除:
|
||||||
|
```csharp
|
||||||
|
list.Remove(2); // 删除第一个匹配到的元素
|
||||||
|
list.RemoveAt(0); // 删除指定下标元素
|
||||||
|
list.Clear(); // 清空整个列表
|
||||||
|
```
|
||||||
|
|
||||||
|
查找:
|
||||||
|
```csharp
|
||||||
|
bool hasValue = list.Contains(99);
|
||||||
|
int index1 = list.IndexOf(99);
|
||||||
|
int index2 = list.LastIndexOf(99);
|
||||||
|
```
|
||||||
|
|
||||||
|
修改:
|
||||||
|
```csharp
|
||||||
|
list[0] = 100;
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 遍历
|
||||||
|
使用 `for`:
|
||||||
|
```csharp
|
||||||
|
List<string> names = new List<string>() { "A", "B", "C" };
|
||||||
|
|
||||||
|
for (int i = 0; i < names.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine(names[i]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 `foreach`:
|
||||||
|
```csharp
|
||||||
|
foreach (string item in names)
|
||||||
|
{
|
||||||
|
Console.WriteLine(item);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
如果只是读取元素,`foreach` 写起来更方便
|
||||||
|
如果需要下标,通常用 `for`
|
||||||
|
|
||||||
|
----
|
||||||
|
# Count 和 Capacity
|
||||||
|
`Count`:
|
||||||
|
当前实际有多少个元素
|
||||||
|
|
||||||
|
`Capacity`:
|
||||||
|
内部数组当前能容纳多少个元素
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>();
|
||||||
|
|
||||||
|
Console.WriteLine(list.Count);
|
||||||
|
Console.WriteLine(list.Capacity);
|
||||||
|
|
||||||
|
list.Add(10);
|
||||||
|
list.Add(20);
|
||||||
|
|
||||||
|
Console.WriteLine(list.Count);
|
||||||
|
Console.WriteLine(list.Capacity);
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
`List<T>` 会自动扩容
|
||||||
|
所以 `Capacity` 往往大于等于 `Count`
|
||||||
|
|
||||||
|
----
|
||||||
|
# 常用成员
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>() { 3, 1, 2, 1 };
|
||||||
|
|
||||||
|
Console.WriteLine(list.Count);
|
||||||
|
Console.WriteLine(list.Contains(2));
|
||||||
|
Console.WriteLine(list.IndexOf(1));
|
||||||
|
Console.WriteLine(list.LastIndexOf(1));
|
||||||
|
|
||||||
|
list.Sort();
|
||||||
|
list.Reverse();
|
||||||
|
```
|
||||||
|
|
||||||
|
常用方法:
|
||||||
|
1. `Add(T item)`:添加一个元素
|
||||||
|
2. `AddRange(IEnumerable<T>)`:添加一组元素
|
||||||
|
3. `Insert(int index, T item)`:插入元素
|
||||||
|
4. `Remove(T item)`:删除第一个匹配项
|
||||||
|
5. `RemoveAt(int index)`:按下标删除
|
||||||
|
6. `Clear()`:清空
|
||||||
|
7. `Contains(T item)`:判断是否存在
|
||||||
|
8. `IndexOf(T item)`:正向查找下标
|
||||||
|
9. `LastIndexOf(T item)`:反向查找下标
|
||||||
|
10. `Sort()`:排序
|
||||||
|
11. `Reverse()`:反转
|
||||||
|
|
||||||
|
----
|
||||||
|
# 排序和反转
|
||||||
|
排序:
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>() { 3, 5, 1, 2 };
|
||||||
|
list.Sort();
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
```text
|
||||||
|
1 2 3 5
|
||||||
|
```
|
||||||
|
|
||||||
|
反转:
|
||||||
|
```csharp
|
||||||
|
list.Reverse();
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
```text
|
||||||
|
5 3 2 1
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
`Sort()` 默认按从小到大排序
|
||||||
|
`Reverse()` 是把当前顺序直接反过来
|
||||||
|
|
||||||
|
----
|
||||||
|
# 插入和删除时要注意下标
|
||||||
|
错误示例:
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>();
|
||||||
|
list.Add(10);
|
||||||
|
|
||||||
|
// list[1] = 20;
|
||||||
|
// list.RemoveAt(5);
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
下标越界会报错
|
||||||
|
|
||||||
|
更稳的写法:
|
||||||
|
```csharp
|
||||||
|
if (list.Count > 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine(list[0]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
`List<T>` 虽然长度可变
|
||||||
|
但访问下标时仍然必须合法
|
||||||
|
|
||||||
|
----
|
||||||
|
# 泛型的好处
|
||||||
|
```csharp
|
||||||
|
List<int> numbers = new List<int>();
|
||||||
|
numbers.Add(10);
|
||||||
|
// numbers.Add("hello");
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
`List<int>` 只能存 `int`
|
||||||
|
像 `"hello"` 这种类型不匹配的值,编译阶段就会报错
|
||||||
|
|
||||||
|
优点:
|
||||||
|
1. 类型安全
|
||||||
|
2. 减少强制类型转换
|
||||||
|
3. 代码提示更清楚
|
||||||
|
4. 更适合实际开发
|
||||||
|
|
||||||
|
----
|
||||||
|
# 基础案例:怪物列表
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Monster monster1 = new Boss();
|
||||||
|
Monster monster2 = new Goblin();
|
||||||
|
Monster monster3 = new Boss();
|
||||||
|
Monster monster4 = new Goblin();
|
||||||
|
|
||||||
|
for (int i = 0; i < Monster.Monsters.Count; i++)
|
||||||
|
{
|
||||||
|
Monster.Monsters[i].Atk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Monster
|
||||||
|
{
|
||||||
|
public static List<Monster> Monsters = new List<Monster>();
|
||||||
|
|
||||||
|
public Monster()
|
||||||
|
{
|
||||||
|
Monsters.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Atk();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Boss : Monster
|
||||||
|
{
|
||||||
|
public override void Atk()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Boss Atk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Goblin : Monster
|
||||||
|
{
|
||||||
|
public override void Atk()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Goblin Atk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
```text
|
||||||
|
Boss Atk
|
||||||
|
Goblin Atk
|
||||||
|
Boss Atk
|
||||||
|
Goblin Atk
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
1. `List<Monster>` 用来统一保存所有怪物对象
|
||||||
|
2. 因为 `Boss` 和 `Goblin` 都继承 `Monster`
|
||||||
|
3. 所以它们都能放进 `List<Monster>` 中
|
||||||
|
4. 再通过遍历统一调用攻击方法
|
||||||
|
|
||||||
|
----
|
||||||
|
# 适合场景
|
||||||
|
1. 玩家背包中的物品列表
|
||||||
|
2. 敌人列表、角色列表、任务列表
|
||||||
|
3. 聊天消息列表
|
||||||
|
4. 成绩列表、订单列表、日志列表
|
||||||
|
|
||||||
|
一句话:
|
||||||
|
只要你需要“数量不固定的一组同类型数据”
|
||||||
|
通常都可以先考虑 `List<T>`
|
||||||
|
|
||||||
|
----
|
||||||
|
# 注意
|
||||||
|
1. `List<T>` 在 `System.Collections.Generic` 命名空间中
|
||||||
|
2. `List<T>` 是泛型集合,`T` 表示元素类型
|
||||||
|
3. `Count` 是实际元素个数,不是容量
|
||||||
|
4. `Capacity` 是当前容量,可能大于 `Count`
|
||||||
|
5. `Add` 是追加到末尾
|
||||||
|
6. `Insert` 是插入到指定位置
|
||||||
|
7. `Remove` 删除的是第一个匹配项
|
||||||
|
8. `RemoveAt` 删除的是指定下标元素
|
||||||
|
9. `List<T>` 很常用,但按下标中间插入/删除很多次时会有移动开销
|
||||||
|
|
||||||
|
----
|
||||||
|
# 易错点
|
||||||
|
1. `List<T>` 是类,不是数组,但用法上和数组有点像
|
||||||
|
2. `Count` 不是 `Length`
|
||||||
|
3. `Capacity` 不是当前元素数量
|
||||||
|
4. `foreach` 遍历时一般不要直接改集合内容
|
||||||
|
5. 下标访问、`Insert`、`RemoveAt` 都要注意越界
|
||||||
|
|
||||||
|
错误示例:
|
||||||
|
```csharp
|
||||||
|
List<int> list = new List<int>() { 10, 20, 30 };
|
||||||
|
|
||||||
|
// Console.WriteLine(list[3]);
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
下标从 `0` 开始
|
||||||
|
上面这个列表只有 `0`、`1`、`2` 三个有效下标
|
||||||
|
|
||||||
|
----
|
||||||
|
# 一句话记忆
|
||||||
|
==List<T> 是长度可变、类型安全、最常用的泛型列表集合。==
|
||||||
|
|
||||||
|
----
|
||||||
|
# 面试/复习时怎么说
|
||||||
|
可以这样答:
|
||||||
|
|
||||||
|
“`List<T>` 是 C# 中非常常用的泛型集合,本质上可以理解成长度可变的数组。
|
||||||
|
它支持按下标访问,也支持方便的增删查改,比如 `Add`、`Insert`、`Remove`、`RemoveAt`。
|
||||||
|
相比 `ArrayList`,`List<T>` 类型更安全,通常不需要强制类型转换,所以在实际开发中更常用。”
|
||||||
|
|
||||||
|
----
|
||||||
|
引用:
|
||||||
|
1. Microsoft Learn: [List<T> Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=net-9.0)
|
||||||
|
2. Microsoft Learn: [System.Collections.Generic Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic?view=net-9.0)
|
||||||
|
|
||||||
|
#List
|
||||||
|
#泛型集合
|
||||||
|
#集合
|
||||||
|
#进阶
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
# 泛型约束
|
||||||
|
概念:
|
||||||
|
泛型默认可以接收很多类型
|
||||||
|
如果我们希望 `T` 只能是“某一类类型”,就要加==泛型约束==
|
||||||
|
|
||||||
|
作用:
|
||||||
|
1. 限制泛型参数的取值范围
|
||||||
|
2. 让编译器提前检查类型是否符合要求
|
||||||
|
3. 让我们可以安全地使用某些成员或写法
|
||||||
|
4. 比如 `new T()`、调用接口方法、访问父类成员
|
||||||
|
|
||||||
|
关键字:
|
||||||
|
```csharp
|
||||||
|
where
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 通常用法
|
||||||
|
1. 限制泛型必须是引用类型或值类型
|
||||||
|
2. 限制泛型必须继承某个类
|
||||||
|
3. 限制泛型必须实现某个接口
|
||||||
|
4. 限制泛型必须有无参构造函数
|
||||||
|
5. 在工具类、工厂类、集合封装、通用算法中很常见
|
||||||
|
|
||||||
|
简单理解:
|
||||||
|
泛型像“留空的类型位置”
|
||||||
|
约束就是给这个空位加规则
|
||||||
|
不是任何类型都能随便传进来
|
||||||
|
|
||||||
|
----
|
||||||
|
# 语法
|
||||||
|
类上的泛型约束:
|
||||||
|
```csharp
|
||||||
|
class 类名<T> where T : 约束
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
方法上的泛型约束:
|
||||||
|
```csharp
|
||||||
|
void 方法名<T>() where T : 约束
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
多个泛型参数分别约束:
|
||||||
|
```csharp
|
||||||
|
class Test<T, K>
|
||||||
|
where T : class
|
||||||
|
where K : new()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
多个约束组合写法:
|
||||||
|
```csharp
|
||||||
|
class Test<T> where T : 父类名, 接口名, new()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 常见约束
|
||||||
|
常见的基础约束有 6 种:
|
||||||
|
|
||||||
|
| 约束写法 | 含义 |
|
||||||
|
|---|---|
|
||||||
|
| `where T : struct` | `T` 必须是值类型 |
|
||||||
|
| `where T : class` | `T` 必须是引用类型 |
|
||||||
|
| `where T : new()` | `T` 必须有公共无参构造函数 |
|
||||||
|
| `where T : 类名` | `T` 必须是某个类或它的子类 |
|
||||||
|
| `where T : 接口名` | `T` 必须实现某个接口 |
|
||||||
|
| `where T : U` | `T` 必须是另一个泛型参数 `U` 或它的派生类型 |
|
||||||
|
|
||||||
|
补充:
|
||||||
|
现在 C# 里还会见到一些新约束,比如:
|
||||||
|
`class?`、`notnull`、`unmanaged`、`default`
|
||||||
|
入门和面试里最常见的,还是前面这 6 种
|
||||||
|
|
||||||
|
----
|
||||||
|
# 1. struct 约束
|
||||||
|
作用:
|
||||||
|
要求泛型参数必须是==值类型==
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Test<T> where T : struct
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
正确:
|
||||||
|
```csharp
|
||||||
|
Test<int> t1 = new Test<int>();
|
||||||
|
Test<bool> t2 = new Test<bool>();
|
||||||
|
```
|
||||||
|
|
||||||
|
错误:
|
||||||
|
```csharp
|
||||||
|
// Test<string> t3 = new Test<string>();
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
`int`、`float`、`bool`、`char`、结构体 都属于值类型
|
||||||
|
|
||||||
|
----
|
||||||
|
# 2. class 约束
|
||||||
|
作用:
|
||||||
|
要求泛型参数必须是==引用类型==
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Test<T> where T : class
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
正确:
|
||||||
|
```csharp
|
||||||
|
Test<string> t1 = new Test<string>();
|
||||||
|
Test<object> t2 = new Test<object>();
|
||||||
|
```
|
||||||
|
|
||||||
|
错误:
|
||||||
|
```csharp
|
||||||
|
// Test<int> t3 = new Test<int>();
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
类、数组、字符串、委托等通常都是引用类型
|
||||||
|
|
||||||
|
----
|
||||||
|
# 3. new() 约束
|
||||||
|
作用:
|
||||||
|
要求泛型参数必须有==公共无参构造函数==
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Factory<T> where T : new()
|
||||||
|
{
|
||||||
|
public T Create()
|
||||||
|
{
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
案例:
|
||||||
|
```csharp
|
||||||
|
class Player
|
||||||
|
{
|
||||||
|
public Player()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Factory<Player> factory = new Factory<Player>();
|
||||||
|
Player p = factory.Create();
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
如果不加 `where T : new()`,就不能直接写 `new T()`
|
||||||
|
|
||||||
|
----
|
||||||
|
# 4. 父类约束
|
||||||
|
作用:
|
||||||
|
要求泛型参数必须是==某个类本身或它的子类==
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Animal
|
||||||
|
{
|
||||||
|
public void Eat()
|
||||||
|
{
|
||||||
|
Console.WriteLine("吃东西");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dog : Animal
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test<T> where T : Animal
|
||||||
|
{
|
||||||
|
public void DoSomething(T value)
|
||||||
|
{
|
||||||
|
value.Eat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用:
|
||||||
|
```csharp
|
||||||
|
Test<Animal> t1 = new Test<Animal>();
|
||||||
|
Test<Dog> t2 = new Test<Dog>();
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
因为 `T` 一定是 `Animal` 或其子类
|
||||||
|
所以可以直接把 `T` 当成 `Animal` 来用
|
||||||
|
|
||||||
|
----
|
||||||
|
# 5. 接口约束
|
||||||
|
作用:
|
||||||
|
要求泛型参数必须==实现某个接口==
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
interface IFly
|
||||||
|
{
|
||||||
|
void Fly();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bird : IFly
|
||||||
|
{
|
||||||
|
public void Fly()
|
||||||
|
{
|
||||||
|
Console.WriteLine("飞行");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test<T> where T : IFly
|
||||||
|
{
|
||||||
|
public void StartFly(T value)
|
||||||
|
{
|
||||||
|
value.Fly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用:
|
||||||
|
```csharp
|
||||||
|
Test<Bird> t = new Test<Bird>();
|
||||||
|
t.StartFly(new Bird());
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
因为约束保证了 `T` 一定有 `Fly()`
|
||||||
|
所以在泛型里可以直接调用接口成员
|
||||||
|
|
||||||
|
----
|
||||||
|
# 6. 泛型参数之间的约束
|
||||||
|
作用:
|
||||||
|
让一个泛型参数受另一个泛型参数限制
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Test<T, K> where T : K
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
`T : K` 表示 `T` 必须是 `K` 本身
|
||||||
|
或者是 `K` 的派生类型、实现类型
|
||||||
|
|
||||||
|
案例:
|
||||||
|
```csharp
|
||||||
|
class Animal
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dog : Animal
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Test<Dog, Animal> t = new Test<Dog, Animal>();
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
# 组合约束
|
||||||
|
泛型约束可以组合使用:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Test<T> where T : Animal, IFly, new()
|
||||||
|
{
|
||||||
|
public T CreateAndUse()
|
||||||
|
{
|
||||||
|
T value = new T();
|
||||||
|
value.Eat();
|
||||||
|
value.Fly();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
含义:
|
||||||
|
1. `T` 必须继承 `Animal`
|
||||||
|
2. `T` 必须实现 `IFly`
|
||||||
|
3. `T` 必须有公共无参构造函数
|
||||||
|
|
||||||
|
----
|
||||||
|
# 基础案例
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
|
||||||
|
interface IRun
|
||||||
|
{
|
||||||
|
void Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Player : IRun
|
||||||
|
{
|
||||||
|
public Player()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
Console.WriteLine("玩家正在移动");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Manager<T> where T : IRun, new()
|
||||||
|
{
|
||||||
|
public T Create()
|
||||||
|
{
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(T obj)
|
||||||
|
{
|
||||||
|
obj.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Manager<Player> manager = new Manager<Player>();
|
||||||
|
Player player = manager.Create();
|
||||||
|
manager.Start(player);
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
```text
|
||||||
|
玩家正在移动
|
||||||
|
```
|
||||||
|
|
||||||
|
理解:
|
||||||
|
因为 `T` 同时满足 `IRun` 和 `new()`
|
||||||
|
所以既能 `new T()`,又能调用 `Run()`
|
||||||
|
|
||||||
|
----
|
||||||
|
# 约束能解决什么问题
|
||||||
|
没有约束时:
|
||||||
|
```csharp
|
||||||
|
class Test<T>
|
||||||
|
{
|
||||||
|
public void Show(T value)
|
||||||
|
{
|
||||||
|
// 这里不能随便调用 value 的成员
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
编译器不知道 `T` 到底是什么类型
|
||||||
|
|
||||||
|
加上约束后:
|
||||||
|
```csharp
|
||||||
|
interface IShow
|
||||||
|
{
|
||||||
|
void Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test<T> where T : IShow
|
||||||
|
{
|
||||||
|
public void Print(T value)
|
||||||
|
{
|
||||||
|
value.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
约束的本质,就是告诉编译器:
|
||||||
|
“这个泛型参数至少具备这些能力”
|
||||||
|
|
||||||
|
----
|
||||||
|
# 注意
|
||||||
|
1. 泛型约束使用关键字 `where`
|
||||||
|
2. `new()` 约束表示必须有公共无参构造函数
|
||||||
|
3. 想写 `new T()`,必须加 `where T : new()`
|
||||||
|
4. 父类约束和接口约束可以让我们安全调用成员
|
||||||
|
5. 一个泛型参数可以同时有多个约束
|
||||||
|
6. 如果有 `new()`,通常要写在约束列表最后
|
||||||
|
7. `struct` 和 `class` 一般不能同时使用
|
||||||
|
8. 约束的作用是“限制类型 + 提供可用能力”
|
||||||
|
|
||||||
|
----
|
||||||
|
# 易错点
|
||||||
|
1. `new()` 不是“可以 new 一切”,而是“必须存在公共无参构造函数”
|
||||||
|
2. 有参构造函数不算满足 `new()`
|
||||||
|
3. `where T : 类名` 表示继承这个类,不是“名字像这个类”
|
||||||
|
4. `where T : 接口名` 表示实现接口,不是普通包含同名方法
|
||||||
|
5. 没有约束时,不能因为自己“知道”类型就直接调用成员
|
||||||
|
|
||||||
|
错误示例:
|
||||||
|
```csharp
|
||||||
|
class Person
|
||||||
|
{
|
||||||
|
public Person(string name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory<Person> factory = new Factory<Person>();
|
||||||
|
```
|
||||||
|
|
||||||
|
原因:
|
||||||
|
`Person` 没有公共无参构造函数
|
||||||
|
所以不满足 `new()`
|
||||||
|
|
||||||
|
----
|
||||||
|
# 一句话记忆
|
||||||
|
==泛型约束就是给泛型参数加条件,让它只能使用满足要求的类型。==
|
||||||
|
|
||||||
|
----
|
||||||
|
# 面试/复习时怎么说
|
||||||
|
可以这样答:
|
||||||
|
|
||||||
|
“泛型约束就是用 `where` 给泛型参数加限制。
|
||||||
|
比如可以限制它必须是值类型、引用类型、某个父类、某个接口,或者必须有无参构造函数。
|
||||||
|
这样做的好处是,编译器能提前帮我们检查类型,同时我们在泛型内部也能更安全地 `new T()` 或调用指定成员。”
|
||||||
|
|
||||||
|
----
|
||||||
|
引用:
|
||||||
|
1. Microsoft Learn: [Constraints on type parameters](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters)
|
||||||
|
2. Microsoft Learn: [where (generic type constraint)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint)
|
||||||
|
|
||||||
|
#泛型约束
|
||||||
|
#泛型
|
||||||
|
#CSharp
|
||||||
|
#进阶
|
||||||
|
|||||||
+2
-2
@@ -35,8 +35,8 @@
|
|||||||
3. [[Queue]]
|
3. [[Queue]]
|
||||||
4. [[Hashtable]]
|
4. [[Hashtable]]
|
||||||
5. [[泛型]]和[[泛型约束]]
|
5. [[泛型]]和[[泛型约束]]
|
||||||
6. List
|
6. [[List]]
|
||||||
7. Dictionary
|
7. [[Dictionary]]
|
||||||
8. LinkedList
|
8. LinkedList
|
||||||
9. 泛型栈和队列
|
9. 泛型栈和队列
|
||||||
10. 委托事件
|
10. 委托事件
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
初始化:
|
||||||
|
- git init
|
||||||
|
|
||||||
|
添加远程仓库:
|
||||||
|
- git remote add origin 地址
|
||||||
|
|
||||||
|
把修改加入暂存区:
|
||||||
|
- git add .
|
||||||
|
|
||||||
|
提交修改
|
||||||
|
- git commit -m "备注信息"
|
||||||
|
|
||||||
|
推送到远程仓库
|
||||||
|
- git push
|
||||||
|
- git push -u origin 分支名
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# echo 输出的内容
|
||||||
|
|
||||||
|
作用:在命令行输出指定的内容
|
||||||
|
|
||||||
|
选项:无选项
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- echo "\\"Hello Linux\\""
|
||||||
|
- echo \`pwd\`
|
||||||
|
|
||||||
|
# 反引号/飘号:\`
|
||||||
|
|
||||||
|
作用:把包裹的内容作为命令执行
|
||||||
|
|
||||||
|
# 重定向符 >
|
||||||
|
|
||||||
|
作用:
|
||||||
|
- >:将左侧命令的结果,覆盖写入到右侧指定的文件中
|
||||||
|
- >>:将左侧命令的结果,追加写入到右侧指定的文件中
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- echo "this is a added line" >> test.txt
|
||||||
|
|
||||||
|
# tail \[-f -num] \[路径]
|
||||||
|
|
||||||
|
作用:查看文件尾部内容,追踪文件的最新更改
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- -f:表示持续跟踪
|
||||||
|
- -num:表示查看尾部多少行
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- tail -5 test.txt
|
||||||
|
- tail -f test.txt
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# grep \[-n] \[关键字] \[内容输入]
|
||||||
|
|
||||||
|
作用:从文件中通过关键字过滤文件行
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- -n:可选 表示在结果中显示匹配的行号
|
||||||
|
- 关键字:必填 表示过滤的关键字,带有空格或其他特殊符号用`""包裹起来
|
||||||
|
- 内容输入:必填 表示要过滤内容的文件路径
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- grep -n "kister" ~/test.txt
|
||||||
|
- grep "kister" ~/test.txt
|
||||||
|
|
||||||
|
# wc \[-c -m -l -w] \[内容输入]
|
||||||
|
|
||||||
|
作用:统计文件的行数、单词量等
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- -c:统计bytes数量
|
||||||
|
- -m:统计字符数量
|
||||||
|
- -l:统计行数
|
||||||
|
- -w:统计单词数量
|
||||||
|
- 内容输入:必填 表示要统计内容的文件路径
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- wc test.txt
|
||||||
|
- wc -lw test.txt
|
||||||
|
|
||||||
|
# 管道符
|
||||||
|
|
||||||
|
符号:`|
|
||||||
|
|
||||||
|
作用:将左边命令的结果作为右边命令的输入
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- cat text.txt | grep "kister"
|
||||||
|
- ls -la / | grep "home"
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# 工作模式
|
||||||
|
## 命令模式
|
||||||
|
命令模式下,所有输入都理解为命令,以命令驱动不同的功能
|
||||||
|
输入vi指令后进入
|
||||||
|
## 输入模式
|
||||||
|
正常的编辑模式、插入模式
|
||||||
|
可以对内容进行自由编辑
|
||||||
|
ESC退出
|
||||||
|
## 底线命令模式
|
||||||
|
以:开始,通常用于文件的保存
|
||||||
|
按下:开始
|
||||||
|
|
||||||
|
# 命令:
|
||||||
|
进入命令模式:
|
||||||
|
- vim \[文件]
|
||||||
|
如果文件不存在会自动创建
|
||||||
|
|
||||||
|
|
||||||
|
按下ESC:无论在哪儿都回到命令模式
|
||||||
|
命令模式:
|
||||||
|
- i:进入输入模式
|
||||||
|
- I:在当前行的开头进入输入模式
|
||||||
|
- a:在当前光标位置 的后进入输入模式
|
||||||
|
- A:在当前行的结尾进入输入模式
|
||||||
|
- o:在当前光标下一行进入输入模式
|
||||||
|
- O:在当前光标上一行进入输入模式
|
||||||
|
- -----------------------------
|
||||||
|
- ↑、k:上移
|
||||||
|
- ↓、j:下移
|
||||||
|
- ←、h:左移
|
||||||
|
- →、l:右移
|
||||||
|
- 0:移动到当前行开头
|
||||||
|
- $:移动到当前行结尾
|
||||||
|
- pageup:上翻页
|
||||||
|
- pagedown:下翻页
|
||||||
|
- ---
|
||||||
|
- /:进入搜索模式
|
||||||
|
- n:向下继续搜索
|
||||||
|
- N:向上继续搜索
|
||||||
|
- ---
|
||||||
|
- yy:复制一行
|
||||||
|
- nyy:n是数字,复制多行
|
||||||
|
- P:粘贴
|
||||||
|
- dd:删除一行
|
||||||
|
- ndd:n是数字,删除多行
|
||||||
|
- u:撤销
|
||||||
|
- ctrl+r:撤销 撤销指令
|
||||||
|
- gg:跳到开头
|
||||||
|
- G:跳到结尾
|
||||||
|
- dG:从当前行开始,向下全部删除
|
||||||
|
- dgg:从当前行开始,向上全部删除
|
||||||
|
- d$:从光标开始,删除到本行结尾
|
||||||
|
- d0:从光标开始,删除到本行的开头
|
||||||
|
|
||||||
|
输入模式:
|
||||||
|
- 进行任意内容的更改
|
||||||
|
|
||||||
|
底线命令模式:
|
||||||
|
- 输入`":"`:进行入底线命令模式
|
||||||
|
- w:保存
|
||||||
|
- q:退出
|
||||||
|
- !:强制执行
|
||||||
|
- set nu:显示行号
|
||||||
|
- set paste:设置粘贴模式
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user