diff --git a/主页.md b/主页.md index acb3cd5..7327db3 100644 --- a/主页.md +++ b/主页.md @@ -47,4 +47,5 @@ done today sort by done reverse ``` -* 2026-06-20 - 2 \ No newline at end of file +* 2026-06-20 - 2 +* 2026-06-22 - 2 \ No newline at end of file diff --git a/待办/日常.md b/待办/日常.md index 3ae423b..42ef904 100644 --- a/待办/日常.md +++ b/待办/日常.md @@ -1,15 +1,22 @@ # 日常 List > 这里放每天会自动刷新的小任务。完成后 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 -- [ ] 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 -- [ ] 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 -- [ ] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-21 -- [ ] 学习吉他 🔼 🔁 every day 📅 2026-06-21 +- [ ] 今日复盘:记录完成情况和明天第一件事 🔼 🔁 every day 📅 2026-06-22 +- [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 +- [ ] 主页打卡⏬ 🔁 every day 📅 2026-06-21 ## 显示今日的样例: diff --git a/模板文件夹/每日计划.md b/模板文件夹/每日计划.md index 1ac2bdf..73ce4d7 100644 --- a/模板文件夹/每日计划.md +++ b/模板文件夹/每日计划.md @@ -18,7 +18,7 @@ sort by priority > ==记得把今日任务详细的规划在时间线上↓== -- [ ] 09:00 - 09:30 +- [ ] 14:40 - 15:40 eNSP ## 复盘 diff --git a/笔记/CSharp/进阶/Dictionary.md b/笔记/CSharp/进阶/Dictionary.md new file mode 100644 index 0000000..629ea76 --- /dev/null +++ b/笔记/CSharp/进阶/Dictionary.md @@ -0,0 +1,409 @@ +# Dictionary +概念: + `Dictionary` 是 C# 提供的==泛型键值对集合== + 它通过键 `key` 来快速查找值 `value` + +简单理解: + 像一本字典 + 用“词”查“解释” + 用 `key` 查 `value` + +命名空间: +```csharp +using System.Collections.Generic; +``` + +---- +# 通常用法 +1. 保存键值对数据 +2. 通过唯一键快速查找对应值 +3. 根据 `id`、名字、编号等信息管理对象 +4. 替代早期非泛型键值对集合 `Hashtable` + +实际开发中: + `Dictionary` 非常常用 + 只要需求是“通过 key 快速找到 value” + 通常都可以先考虑它 + +---- +# 语法 +创建: +```csharp +Dictionary dict = new Dictionary(); +``` + +添加: +```csharp +dict.Add("语文", 90); +``` + +通过键取值: +```csharp +int score = dict["语文"]; +``` + +修改: +```csharp +dict["语文"] = 95; +``` + +数量: +```csharp +dict.Count +``` + +---- +# 基础案例 +```csharp +using System; +using System.Collections.Generic; + +Dictionary scores = new Dictionary(); + +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 dict = new Dictionary(); + +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 dict = new Dictionary(); + +dict.Add("语文", 90); +dict.Add("数学", 95); + +foreach (KeyValuePair 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 dict = new Dictionary(); + +dict.Add("hp", 100); +dict.Add("hp", 200); +``` + +原因: + `Dictionary` 中的 key 必须唯一 + 重复添加同一个 key 会报错 + +如果想修改旧值: +```csharp +dict["hp"] = 200; +``` + +---- +# key 一般不能为 null +错误写法: +```csharp +Dictionary dict = new Dictionary(); + +// dict.Add(null, 100); +``` + +原因: + `Dictionary` 的 key 不能为 `null` + +补充: + 如果 `TValue` 是引用类型或可空类型 + value 可以是 `null` + +示例: +```csharp +Dictionary dict = new Dictionary(); +dict["name"] = null; +``` + +---- +# 和 Hashtable 的区别 +`Hashtable`: + 非泛型 + key 和 value 都按 `object` 处理 + 取值时常常需要强制类型转换 + +`Dictionary`: + 泛型 + key 和 value 类型明确 + 编译期就能检查类型错误 + 使用起来更安全,更常见 + +对比表: + +| 对比点 | `Hashtable` | `Dictionary` | +|---|---|---| +| 类型 | 非泛型 | 泛型 | +| 键和值 | `object` | 指定类型 | +| 类型安全 | 较弱 | 更强 | +| 取值 | 常要强转 | 一般不用强转 | +| 推荐程度 | 主要用于学习旧集合 | 实际开发常用 | + +---- +# 和 List 的区别 +| 对比点 | `List` | `Dictionary` | +|---|---|---| +| 存储方式 | 单个元素 | 键值对 | +| 查找方式 | 常用下标 | 常用 key | +| key | 没有 | 必须唯一 | +| 适合场景 | 一组同类型数据 | 根据唯一键查数据 | + +一句话: + `List` 更像“列表” + `Dictionary` 更像“字典” + +---- +# 基础案例:角色表 +```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 players = new Dictionary(); + + 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 dict = new Dictionary(); + +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` 在 `System.Collections.Generic` 命名空间中 +2. 它是泛型键值对集合 +3. key 必须唯一 +4. `Add` 遇到重复 key 会报错 +5. `dict[key] = value` 可以新增,也可以覆盖旧值 +6. 用不存在的 key 直接取值可能报错 +7. 更稳的取值方式通常是 `TryGetValue` +8. 它最核心的优势是“通过 key 快速找 value” + +---- +# 易错点 +1. 不要把 `Dictionary` 当数组或 `List` 来用 +2. 不要默认 key 一定存在 +3. `ContainsKey` 判断的是键,不是值 +4. `ContainsValue` 可以用,但实际开发里更常关心 key +5. 重复 `Add` 同一个 key 会报错 + +错误示例: +```csharp +Dictionary dict = new Dictionary(); +dict.Add("hp", 100); + +// Console.WriteLine(dict["mp"]); +``` + +原因: + `"mp"` 这个 key 不存在 + 直接取值可能报错 + +---- +# 一句话记忆 +==Dictionary 是通过唯一 key 快速查找 value 的泛型键值对集合。== + +再压缩一点: +==List 用下标找,Dictionary 用 key 找。== + +---- +# 面试/复习时怎么说 +可以这样答: + +“`Dictionary` 是 C# 中非常常用的泛型键值对集合,适合通过唯一 key 快速查找 value。 +它的 key 不能重复,常用操作有 `Add`、`Remove`、`ContainsKey` 和 `TryGetValue`。 +相比 `Hashtable`,`Dictionary` 类型更安全,不需要频繁强制类型转换,所以实际开发中更常用。” + +---- +引用: +1. Microsoft Learn: [Dictionary Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-9.0) +2. Microsoft Learn: [Dictionary.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 +#字典 +#泛型集合 +#集合 +#进阶 diff --git a/笔记/CSharp/进阶/List.md b/笔记/CSharp/进阶/List.md new file mode 100644 index 0000000..cff7a26 --- /dev/null +++ b/笔记/CSharp/进阶/List.md @@ -0,0 +1,423 @@ +# List +概念: + `List` 是 C# 提供的==泛型集合类== + 本质上可以理解为:==一个长度可变的泛型数组== + +它可以: +1. 按下标访问元素 +2. 自动扩容 +3. 方便地增删查改 +4. 保证集合中元素类型统一 + +命名空间: +```csharp +using System.Collections.Generic; +``` + +简单理解: + 数组长度固定 + `List` 长度可以动态变化 + 而且比 `ArrayList` 更安全,因为它有明确类型 + +---- +# 通常用法 +1. 存储数量不固定的一组同类型数据 +2. 需要频繁添加、删除、遍历元素 +3. 作为项目里最常见的基础集合之一 +4. 替代早期非泛型集合 `ArrayList` + +实际开发中: + 新项目里 `List` 的使用频率非常高 + 很多时候它就是“默认列表容器” + +---- +# 语法 +创建: +```csharp +List list = new List(); +``` + +创建并赋初值: +```csharp +List names = new List() { "小明", "小红", "小刚" }; +``` + +添加: +```csharp +list.Add(10); +``` + +访问: +```csharp +list[0] +``` + +数量: +```csharp +list.Count +``` + +---- +# 基础案例 +```csharp +using System; +using System.Collections.Generic; + +List list = new List(); + +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` +2. 可以像数组一样通过下标访问 +3. `Count` 表示当前实际元素个数 + +---- +# 和 ArrayList 的区别 +`ArrayList`: + 内部按 `object` 存储 + 可以混合存很多类型 + 取值时常常需要强制类型转换 + 值类型放进去会发生装箱拆箱 + +`List`: + 内部是泛型集合 + 类型更明确 + 编译期就能检查类型错误 + 通常不会像 `ArrayList` 那样频繁发生装箱拆箱 + +对比表: + +| 对比点 | `ArrayList` | `List` | +|---|---|---| +| 类型 | 非泛型 | 泛型 | +| 存储 | `object` | 指定类型 `T` | +| 类型安全 | 较弱 | 更强 | +| 取值 | 常要强转 | 一般不用强转 | +| 性能 | 值类型可能有装箱拆箱 | 通常更好 | +| 推荐程度 | 主要用于学习旧集合 | 实际开发常用 | + +---- +# 增删查改 +增加: +```csharp +List list = new List(); + +list.Add(1); +list.Add(2); +list.Add(3); + +list.Insert(1, 99); +list.AddRange(new List { 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 names = new List() { "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 list = new List(); + +Console.WriteLine(list.Count); +Console.WriteLine(list.Capacity); + +list.Add(10); +list.Add(20); + +Console.WriteLine(list.Count); +Console.WriteLine(list.Capacity); +``` + +理解: + `List` 会自动扩容 + 所以 `Capacity` 往往大于等于 `Count` + +---- +# 常用成员 +```csharp +List list = new List() { 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)`:添加一组元素 +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 list = new List() { 3, 5, 1, 2 }; +list.Sort(); +``` + +结果: +```text +1 2 3 5 +``` + +反转: +```csharp +list.Reverse(); +``` + +结果: +```text +5 3 2 1 +``` + +说明: + `Sort()` 默认按从小到大排序 + `Reverse()` 是把当前顺序直接反过来 + +---- +# 插入和删除时要注意下标 +错误示例: +```csharp +List list = new List(); +list.Add(10); + +// list[1] = 20; +// list.RemoveAt(5); +``` + +原因: + 下标越界会报错 + +更稳的写法: +```csharp +if (list.Count > 0) +{ + Console.WriteLine(list[0]); +} +``` + +说明: + `List` 虽然长度可变 + 但访问下标时仍然必须合法 + +---- +# 泛型的好处 +```csharp +List numbers = new List(); +numbers.Add(10); +// numbers.Add("hello"); +``` + +说明: + `List` 只能存 `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 Monsters = new List(); + + 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` 用来统一保存所有怪物对象 +2. 因为 `Boss` 和 `Goblin` 都继承 `Monster` +3. 所以它们都能放进 `List` 中 +4. 再通过遍历统一调用攻击方法 + +---- +# 适合场景 +1. 玩家背包中的物品列表 +2. 敌人列表、角色列表、任务列表 +3. 聊天消息列表 +4. 成绩列表、订单列表、日志列表 + +一句话: + 只要你需要“数量不固定的一组同类型数据” + 通常都可以先考虑 `List` + +---- +# 注意 +1. `List` 在 `System.Collections.Generic` 命名空间中 +2. `List` 是泛型集合,`T` 表示元素类型 +3. `Count` 是实际元素个数,不是容量 +4. `Capacity` 是当前容量,可能大于 `Count` +5. `Add` 是追加到末尾 +6. `Insert` 是插入到指定位置 +7. `Remove` 删除的是第一个匹配项 +8. `RemoveAt` 删除的是指定下标元素 +9. `List` 很常用,但按下标中间插入/删除很多次时会有移动开销 + +---- +# 易错点 +1. `List` 是类,不是数组,但用法上和数组有点像 +2. `Count` 不是 `Length` +3. `Capacity` 不是当前元素数量 +4. `foreach` 遍历时一般不要直接改集合内容 +5. 下标访问、`Insert`、`RemoveAt` 都要注意越界 + +错误示例: +```csharp +List list = new List() { 10, 20, 30 }; + +// Console.WriteLine(list[3]); +``` + +原因: + 下标从 `0` 开始 + 上面这个列表只有 `0`、`1`、`2` 三个有效下标 + +---- +# 一句话记忆 +==List 是长度可变、类型安全、最常用的泛型列表集合。== + +---- +# 面试/复习时怎么说 +可以这样答: + +“`List` 是 C# 中非常常用的泛型集合,本质上可以理解成长度可变的数组。 +它支持按下标访问,也支持方便的增删查改,比如 `Add`、`Insert`、`Remove`、`RemoveAt`。 +相比 `ArrayList`,`List` 类型更安全,通常不需要强制类型转换,所以在实际开发中更常用。” + +---- +引用: +1. Microsoft Learn: [List 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 +#泛型集合 +#集合 +#进阶 diff --git a/笔记/CSharp/进阶/泛型约束.md b/笔记/CSharp/进阶/泛型约束.md index e69de29..b470d35 100644 --- a/笔记/CSharp/进阶/泛型约束.md +++ b/笔记/CSharp/进阶/泛型约束.md @@ -0,0 +1,426 @@ +# 泛型约束 +概念: + 泛型默认可以接收很多类型 + 如果我们希望 `T` 只能是“某一类类型”,就要加==泛型约束== + +作用: + 1. 限制泛型参数的取值范围 + 2. 让编译器提前检查类型是否符合要求 + 3. 让我们可以安全地使用某些成员或写法 + 4. 比如 `new T()`、调用接口方法、访问父类成员 + +关键字: +```csharp +where +``` + +---- +# 通常用法 +1. 限制泛型必须是引用类型或值类型 +2. 限制泛型必须继承某个类 +3. 限制泛型必须实现某个接口 +4. 限制泛型必须有无参构造函数 +5. 在工具类、工厂类、集合封装、通用算法中很常见 + +简单理解: + 泛型像“留空的类型位置” + 约束就是给这个空位加规则 + 不是任何类型都能随便传进来 + +---- +# 语法 +类上的泛型约束: +```csharp +class 类名 where T : 约束 +{ +} +``` + +方法上的泛型约束: +```csharp +void 方法名() where T : 约束 +{ +} +``` + +多个泛型参数分别约束: +```csharp +class Test + where T : class + where K : new() +{ +} +``` + +多个约束组合写法: +```csharp +class Test 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 where T : struct +{ +} +``` + +正确: +```csharp +Test t1 = new Test(); +Test t2 = new Test(); +``` + +错误: +```csharp +// Test t3 = new Test(); +``` + +说明: + `int`、`float`、`bool`、`char`、结构体 都属于值类型 + +---- +# 2. class 约束 +作用: + 要求泛型参数必须是==引用类型== + +```csharp +class Test where T : class +{ +} +``` + +正确: +```csharp +Test t1 = new Test(); +Test t2 = new Test(); +``` + +错误: +```csharp +// Test t3 = new Test(); +``` + +说明: + 类、数组、字符串、委托等通常都是引用类型 + +---- +# 3. new() 约束 +作用: + 要求泛型参数必须有==公共无参构造函数== + +```csharp +class Factory where T : new() +{ + public T Create() + { + return new T(); + } +} +``` + +案例: +```csharp +class Player +{ + public Player() + { + } +} + +Factory factory = new Factory(); +Player p = factory.Create(); +``` + +说明: + 如果不加 `where T : new()`,就不能直接写 `new T()` + +---- +# 4. 父类约束 +作用: + 要求泛型参数必须是==某个类本身或它的子类== + +```csharp +class Animal +{ + public void Eat() + { + Console.WriteLine("吃东西"); + } +} + +class Dog : Animal +{ +} + +class Test where T : Animal +{ + public void DoSomething(T value) + { + value.Eat(); + } +} +``` + +使用: +```csharp +Test t1 = new Test(); +Test t2 = new Test(); +``` + +说明: + 因为 `T` 一定是 `Animal` 或其子类 + 所以可以直接把 `T` 当成 `Animal` 来用 + +---- +# 5. 接口约束 +作用: + 要求泛型参数必须==实现某个接口== + +```csharp +interface IFly +{ + void Fly(); +} + +class Bird : IFly +{ + public void Fly() + { + Console.WriteLine("飞行"); + } +} + +class Test where T : IFly +{ + public void StartFly(T value) + { + value.Fly(); + } +} +``` + +使用: +```csharp +Test t = new Test(); +t.StartFly(new Bird()); +``` + +说明: + 因为约束保证了 `T` 一定有 `Fly()` + 所以在泛型里可以直接调用接口成员 + +---- +# 6. 泛型参数之间的约束 +作用: + 让一个泛型参数受另一个泛型参数限制 + +```csharp +class Test where T : K +{ +} +``` + +理解: + `T : K` 表示 `T` 必须是 `K` 本身 + 或者是 `K` 的派生类型、实现类型 + +案例: +```csharp +class Animal +{ +} + +class Dog : Animal +{ +} + +Test t = new Test(); +``` + +---- +# 组合约束 +泛型约束可以组合使用: + +```csharp +class Test 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 where T : IRun, new() +{ + public T Create() + { + return new T(); + } + + public void Start(T obj) + { + obj.Run(); + } +} + +Manager manager = new Manager(); +Player player = manager.Create(); +manager.Start(player); +``` + +输出: +```text +玩家正在移动 +``` + +理解: + 因为 `T` 同时满足 `IRun` 和 `new()` + 所以既能 `new T()`,又能调用 `Run()` + +---- +# 约束能解决什么问题 +没有约束时: +```csharp +class Test +{ + public void Show(T value) + { + // 这里不能随便调用 value 的成员 + } +} +``` + +原因: + 编译器不知道 `T` 到底是什么类型 + +加上约束后: +```csharp +interface IShow +{ + void Show(); +} + +class Test 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 factory = new Factory(); +``` + +原因: + `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 +#进阶 diff --git a/笔记/CSharp笔记汇总.md b/笔记/CSharp笔记汇总.md index 491f3e9..8fb2d6b 100644 --- a/笔记/CSharp笔记汇总.md +++ b/笔记/CSharp笔记汇总.md @@ -35,8 +35,8 @@ 3. [[Queue]] 4. [[Hashtable]] 5. [[泛型]]和[[泛型约束]] -6. List -7. Dictionary +6. [[List]] +7. [[Dictionary]] 8. LinkedList 9. 泛型栈和队列 10. 委托事件 diff --git a/笔记/Git/命令.md b/笔记/Git/命令.md new file mode 100644 index 0000000..803c455 --- /dev/null +++ b/笔记/Git/命令.md @@ -0,0 +1,15 @@ +初始化: + - git init + +添加远程仓库: +- git remote add origin 地址 + +把修改加入暂存区: +- git add . + +提交修改 +- git commit -m "备注信息" + +推送到远程仓库 +- git push +- git push -u origin 分支名 \ No newline at end of file diff --git a/笔记/Linux/基础指令/echo&tail&重定向符.md b/笔记/Linux/基础指令/echo&tail&重定向符.md new file mode 100644 index 0000000..28634a7 --- /dev/null +++ b/笔记/Linux/基础指令/echo&tail&重定向符.md @@ -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 \ No newline at end of file diff --git a/笔记/Linux/基础指令/grep&wc&管道符.md b/笔记/Linux/基础指令/grep&wc&管道符.md new file mode 100644 index 0000000..f08097e --- /dev/null +++ b/笔记/Linux/基础指令/grep&wc&管道符.md @@ -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" \ No newline at end of file diff --git a/笔记/Linux/命令格式.md b/笔记/Linux/基础指令/命令格式.md similarity index 100% rename from 笔记/Linux/命令格式.md rename to 笔记/Linux/基础指令/命令格式.md diff --git a/笔记/Linux/通配符.md b/笔记/Linux/基础指令/通配符.md similarity index 100% rename from 笔记/Linux/通配符.md rename to 笔记/Linux/基础指令/通配符.md diff --git a/笔记/Linux/工具/Vi&Vim编辑器.md b/笔记/Linux/工具/Vi&Vim编辑器.md new file mode 100644 index 0000000..39751b2 --- /dev/null +++ b/笔记/Linux/工具/Vi&Vim编辑器.md @@ -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:设置粘贴模式 + + + diff --git a/笔记/Linux/系统/root用户.md b/笔记/Linux/系统/root用户.md new file mode 100644 index 0000000..e69de29 diff --git a/笔记/Linux/快捷键.md b/笔记/Linux/系统/快捷键.md similarity index 100% rename from 笔记/Linux/快捷键.md rename to 笔记/Linux/系统/快捷键.md diff --git a/笔记/Linux/系统/用户和用户组.md b/笔记/Linux/系统/用户和用户组.md new file mode 100644 index 0000000..e69de29 diff --git a/笔记/Linux/目录结构.md b/笔记/Linux/系统/目录结构.md similarity index 100% rename from 笔记/Linux/目录结构.md rename to 笔记/Linux/系统/目录结构.md