diff --git a/.obsidian/app.json b/.obsidian/app.json index 6abe4c1..9e26dfe 100644 --- a/.obsidian/app.json +++ b/.obsidian/app.json @@ -1,3 +1 @@ -{ - "alwaysUpdateLinks": true -} \ No newline at end of file +{} \ No newline at end of file diff --git a/笔记/CSharp/进阶/事件.md b/笔记/CSharp/进阶/事件.md new file mode 100644 index 0000000..f34b1c6 --- /dev/null +++ b/笔记/CSharp/进阶/事件.md @@ -0,0 +1,467 @@ +# 事件 +概念: + 事件是==基于委托的安全封装== + 它用于表示“某件事发生了”,并通知订阅这个事件的方法 + +简单理解: + 委托像一个可以直接调用的方法列表 + 事件像一个“通知开关” + 类外部可以订阅通知,但不能随意触发通知 + +作用: +1. 当某个状态变化时通知其他对象 +2. 降低对象之间的耦合 +3. 保护委托,避免类外部随意赋值或调用 +4. 常用于按钮点击、游戏状态变化、网络消息、任务完成等场景 + +关键字: +```csharp +event +``` + +---- +# 和委托的关系 +事件的底层是委托: + +```csharp +public event Action OnFinished; +``` + +这里: + `Action` 是委托类型 + `OnFinished` 是事件名 + +可以和[[委托]]一起理解: + 委托负责“存方法和调用方法” + 事件负责“安全地把通知发布给外部” + +---- +# 委托和事件的区别 +直接公开委托: +```csharp +public Action OnFinished; +``` + +类外部可以: +```csharp +task.OnFinished = null; +task.OnFinished?.Invoke(); +``` + +问题: + 外部可以清空委托 + 外部也可以擅自触发通知 + +使用事件: +```csharp +public event Action OnFinished; +``` + +类外部只能: +```csharp +task.OnFinished += ShowMessage; +task.OnFinished -= ShowMessage; +``` + +类外部不能: +```csharp +// task.OnFinished = null; +// task.OnFinished?.Invoke(); +``` + +说明: + 事件让“触发通知”的控制权保留在声明事件的类内部 + +---- +# 语法 +声明事件: +```csharp +访问修饰符 event 委托类型 事件名; +``` + +使用自定义委托: +```csharp +public delegate void FinishHandler(); + +public event FinishHandler OnFinished; +``` + +使用 `Action`: +```csharp +public event Action OnFinished; +``` + +使用 `EventHandler`: +```csharp +public event EventHandler OnFinished; +``` + +---- +# 基础案例 +```csharp +using System; + +class TaskRunner +{ + public event Action OnFinished; + + public void Run() + { + Console.WriteLine("任务执行中"); + Console.WriteLine("任务完成"); + + OnFinished?.Invoke(); + } +} + +class Program +{ + static void ShowMessage() + { + Console.WriteLine("收到任务完成通知"); + } + + static void Main(string[] args) + { + TaskRunner task = new TaskRunner(); + task.OnFinished += ShowMessage; + + task.Run(); + } +} +``` + +输出: +```text +任务执行中 +任务完成 +收到任务完成通知 +``` + +理解: +1. `TaskRunner` 负责执行任务 +2. 任务完成后,在类内部触发 `OnFinished` +3. `ShowMessage` 订阅了事件 +4. 所以任务完成时,会自动执行 `ShowMessage` + +---- +# 订阅和取消订阅 +订阅事件: +```csharp +task.OnFinished += ShowMessage; +``` + +取消订阅: +```csharp +task.OnFinished -= ShowMessage; +``` + +说明: +1. `+=` 表示订阅事件 +2. `-=` 表示取消订阅 +3. 可以有多个方法订阅同一个事件 +4. 取消订阅时,方法必须和订阅时是同一个方法 + +---- +# 一个事件可以有多个订阅者 +```csharp +using System; + +class TaskRunner +{ + public event Action OnFinished; + + public void Run() + { + Console.WriteLine("任务完成"); + OnFinished?.Invoke(); + } +} + +class Program +{ + static void ShowMessage() + { + Console.WriteLine("显示完成提示"); + } + + static void SaveRecord() + { + Console.WriteLine("保存完成记录"); + } + + static void Main(string[] args) + { + TaskRunner task = new TaskRunner(); + + task.OnFinished += ShowMessage; + task.OnFinished += SaveRecord; + + task.Run(); + } +} +``` + +输出: +```text +任务完成 +显示完成提示 +保存完成记录 +``` + +理解: + 同一个事件可以通知多个订阅者 + 每个订阅的方法都会被依次调用 + +---- +# 触发事件时要判空 +不安全写法: +```csharp +OnFinished(); +``` + +问题: + 如果没有任何方法订阅事件 + `OnFinished` 就可能为 `null` + 直接调用会报错 + +常见写法: +```csharp +if (OnFinished != null) +{ + OnFinished(); +} +``` + +更简洁的写法: +```csharp +OnFinished?.Invoke(); +``` + +说明: + `?.Invoke()` 表示事件不为 `null` 时才调用 + +---- +# 带参数的事件 +事件可以使用带参数的 `Action`: + +```csharp +using System; + +class ScoreSystem +{ + public event Action OnScoreChanged; + + public void AddScore(int score) + { + OnScoreChanged?.Invoke(score); + } +} + +class Program +{ + static void ShowScore(int score) + { + Console.WriteLine("获得分数:" + score); + } + + static void Main(string[] args) + { + ScoreSystem system = new ScoreSystem(); + system.OnScoreChanged += ShowScore; + + system.AddScore(100); + } +} +``` + +输出: +```text +获得分数:100 +``` + +说明: + `Action` 表示订阅方法需要接收一个 `int` 参数 + +---- +# EventHandler 标准写法 +在 .NET 中,事件常使用 `EventHandler`: + +```csharp +using System; + +class TaskRunner +{ + public event EventHandler Finished; + + public void Run() + { + Console.WriteLine("任务完成"); + Finished?.Invoke(this, EventArgs.Empty); + } +} + +class Program +{ + static void OnTaskFinished(object sender, EventArgs e) + { + Console.WriteLine("收到任务完成通知"); + } + + static void Main(string[] args) + { + TaskRunner task = new TaskRunner(); + task.Finished += OnTaskFinished; + + task.Run(); + } +} +``` + +说明: +1. `sender` 表示触发事件的对象 +2. `EventArgs.Empty` 表示没有额外事件数据 +3. `EventHandler` 是 .NET 中常见的事件委托类型 + +---- +# EventHandler +如果事件需要传更多信息,可以自定义事件参数类: + +```csharp +using System; + +class ScoreChangedEventArgs : EventArgs +{ + public int Score { get; } + + public ScoreChangedEventArgs(int score) + { + Score = score; + } +} + +class ScoreSystem +{ + public event EventHandler ScoreChanged; + + public void AddScore(int score) + { + ScoreChanged?.Invoke(this, new ScoreChangedEventArgs(score)); + } +} + +class Program +{ + static void OnScoreChanged(object sender, ScoreChangedEventArgs e) + { + Console.WriteLine("获得分数:" + e.Score); + } + + static void Main(string[] args) + { + ScoreSystem system = new ScoreSystem(); + system.ScoreChanged += OnScoreChanged; + + system.AddScore(100); + } +} +``` + +输出: +```text +获得分数:100 +``` + +理解: + `ScoreChangedEventArgs` 专门用来保存事件附带的数据 + 订阅方法通过 `e.Score` 获取具体信息 + +---- +# 推荐的触发方式 +事件一般由声明它的类负责触发: + +```csharp +class TaskRunner +{ + public event EventHandler Finished; + + protected virtual void OnFinished() + { + Finished?.Invoke(this, EventArgs.Empty); + } + + public void Run() + { + Console.WriteLine("任务完成"); + OnFinished(); + } +} +``` + +好处: +1. 把“触发事件”的代码集中在 `OnFinished` +2. 子类可以通过重写 `OnFinished` 扩展行为 +3. 事件命名和触发逻辑更清晰 + +---- +# 常见使用场景 +1. 按钮点击后通知界面逻辑 +2. 任务完成后刷新状态 +3. 分数、血量、进度变化后更新显示 +4. 网络消息到达后通知处理模块 +5. 下载完成、计时结束、加载完成等通知 + +---- +# 注意 +1. 事件的底层是委托 +2. 事件只能声明在类、结构体或接口中 +3. 类外部只能使用 `+=` 订阅、`-=` 取消订阅 +4. 类外部不能直接赋值事件,也不能直接触发事件 +5. 事件可能没有订阅者,触发前要判空 +6. 简单事件可用 `Action` +7. 标准 .NET 事件常用 `EventHandler` 或 `EventHandler` +8. 不再需要监听时,应及时取消订阅 + +---- +# 易错点 +1. 事件不是独立机制,它本质上依赖委托 +2. `event` 不能让外部直接调用,只能让声明类内部触发 +3. `+=` 是订阅,不是普通赋值 +4. 事件没有订阅者时,直接调用可能出现空引用异常 +5. 订阅后忘记 `-=`,可能导致对象一直被引用 + +错误示例: +```csharp +TaskRunner task = new TaskRunner(); + +// task.OnFinished?.Invoke(); +// task.OnFinished = null; +``` + +原因: + `OnFinished` 是事件 + 外部只能订阅或取消订阅,不能触发或覆盖它 + +---- +# 一句话记忆 +==事件是对委托的安全封装,用来在事情发生时通知订阅者。== + +---- +# 面试/复习时怎么说 +可以这样答: + +“事件本质上是对委托的封装,用于发布通知。 +外部对象可以通过 `+=` 订阅事件、通过 `-=` 取消订阅,但不能直接触发或覆盖事件。 +这样可以把触发权保留在事件发布者内部,常用于按钮点击、状态变化和任务完成等场景。” + +---- +引用: +1. Microsoft Learn: [Events](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/) +2. Microsoft Learn: [EventHandler Delegate](https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler) +3. Microsoft Learn: [EventHandler Delegate](https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler-1) + +#事件 +#event +#委托 +#EventHandler +#进阶 diff --git a/笔记/CSharp/进阶/委托.md b/笔记/CSharp/进阶/委托.md new file mode 100644 index 0000000..ba141ca --- /dev/null +++ b/笔记/CSharp/进阶/委托.md @@ -0,0 +1,426 @@ +# 委托 +概念: + 委托是==可以引用方法的类型== + 可以理解为“函数的容器”或“存方法的变量” + +作用: +1. 把方法赋值给变量 +2. 把方法作为参数传递 +3. 让一个地方可以调用多个方法 +4. 为事件、匿名函数、lambda 提供基础 + +简单理解: + 普通变量存数据 + 委托变量存“符合条件的方法” + +---- +# 通常用法 +1. 作为类的成员,保存回调方法 +2. 作为方法参数,让调用者决定具体执行什么逻辑 +3. 同时绑定多个方法,实现多播调用 +4. 配合 `Action`、`Func`、匿名函数和 lambda 使用 + +实际开发中: + 自定义委托仍然会用到 + 但很多简单场景更常使用系统自带的 `Action` 和 `Func` + +---- +# 语法 +关键字: +```csharp +delegate +``` + +定义委托: +```csharp +访问修饰符 delegate 返回值类型 委托名(参数列表); +``` + +示例: +```csharp +public delegate void MyDelegate(string message); +``` + +说明: + `MyDelegate` 只能保存: + 参数是 `string` + 返回值是 `void` + 的方法 + +---- +# 基础案例 +```csharp +using System; + +public delegate void MyDelegate(string message); + +class Program +{ + static void PrintMessage(string message) + { + Console.WriteLine(message); + } + + static void Main(string[] args) + { + MyDelegate del = PrintMessage; + del("你好,委托"); + } +} +``` + +输出: +```text +你好,委托 +``` + +理解: +1. `MyDelegate` 规定了方法的格式 +2. `PrintMessage` 的参数和返回值符合这个格式 +3. 所以可以赋值给 `del` +4. 调用 `del(...)`,本质上就是调用 `PrintMessage(...)` + +---- +# 方法必须匹配委托格式 +```csharp +public delegate int Calculate(int a, int b); + +static int Add(int a, int b) +{ + return a + b; +} + +Calculate calculate = Add; +Console.WriteLine(calculate(10, 20)); +``` + +正确匹配: + 参数数量一致 + 参数类型一致 + 返回值类型一致 + +错误示例: +```csharp +static void Print(int a, int b) +{ +} + +// Calculate calculate = Print; +``` + +原因: + `Calculate` 要求返回 `int` + `Print` 的返回值是 `void` + 两者不匹配 + +---- +# 委托的赋值和调用 +赋值: +```csharp +MyDelegate del = PrintMessage; +``` + +调用: +```csharp +del("hello"); +``` + +也可以使用 `Invoke`: +```csharp +del.Invoke("hello"); +``` + +通常写法: + 直接写 `del(...)` 更常见 + `Invoke(...)` 的语义更明确 + +---- +# 委托可以作为参数 +作用: + 把“具体要做什么”交给调用者决定 + +```csharp +using System; + +public delegate void HandleNumber(int number); + +class Program +{ + static void Process(int number, HandleNumber handler) + { + handler(number); + } + + static void PrintNumber(int number) + { + Console.WriteLine("数字:" + number); + } + + static void PrintDouble(int number) + { + Console.WriteLine("两倍:" + number * 2); + } + + static void Main(string[] args) + { + Process(10, PrintNumber); + Process(10, PrintDouble); + } +} +``` + +输出: +```text +数字:10 +两倍:20 +``` + +理解: + `Process` 不关心具体处理方式 + 只要求传进来的方法符合 `HandleNumber` 的格式 + +---- +# 多播委托 +一个委托变量可以同时绑定多个方法: + +```csharp +using System; + +public delegate void MyDelegate(); + +class Program +{ + static void Method1() + { + Console.WriteLine("执行方法1"); + } + + static void Method2() + { + Console.WriteLine("执行方法2"); + } + + static void Main(string[] args) + { + MyDelegate del = Method1; + del += Method2; + + del(); + } +} +``` + +输出: +```text +执行方法1 +执行方法2 +``` + +取消绑定: +```csharp +del -= Method2; +``` + +说明: +1. `+=` 用来添加方法 +2. `-=` 用来移除方法 +3. 调用时会按添加顺序依次执行 +4. 多播委托最适合返回值为 `void` 的情况 + +---- +# 有返回值的多播委托 +```csharp +public delegate int Calculate(); + +static int Method1() +{ + return 1; +} + +static int Method2() +{ + return 2; +} + +Calculate calculate = Method1; +calculate += Method2; + +int result = calculate(); +Console.WriteLine(result); +``` + +输出: +```text +2 +``` + +说明: + 两个方法都会执行 + 但委托调用的最终返回值,只会保留最后一个方法的返回值 + +所以: + 多播委托通常更适合 `void` 返回值 + +---- +# 空委托要先判断 +错误示例: +```csharp +MyDelegate del = null; + +// del(); +``` + +原因: + 委托变量是引用类型 + 没有绑定方法时可能为 `null` + +常见写法: +```csharp +if (del != null) +{ + del(); +} +``` + +更简洁的写法: +```csharp +del?.Invoke(); +``` + +说明: + `?.` 会先判断 `del` 是否为 `null` + 不为 `null` 才调用 `Invoke()` + +---- +# Action +`Action` 是系统自带的委托类型: + ==无返回值== + +无参: +```csharp +Action action = PrintHello; +action(); + +static void PrintHello() +{ + Console.WriteLine("Hello"); +} +``` + +有参数: +```csharp +Action print = PrintMessage; +print("你好"); + +static void PrintMessage(string message) +{ + Console.WriteLine(message); +} +``` + +说明: + `Action` 后面的泛型参数表示方法参数类型 + 它的返回值始终是 `void` + +---- +# Func +`Func` 是系统自带的泛型委托类型: + ==有返回值== + +```csharp +Func add = Add; + +int result = add(10, 20); +Console.WriteLine(result); + +static int Add(int a, int b) +{ + return a + b; +} +``` + +说明: +```csharp +Func<参数1类型, 参数2类型, 返回值类型> +``` + +上面的: +```csharp +Func +``` + +表示: + 前两个 `int` 是参数类型 + 最后一个 `int` 是返回值类型 + +---- +# 自定义委托、Action、Func 的区别 +| 对比点 | 自定义委托 | `Action` | `Func` | +|---|---|---|---| +| 是否需要自己声明 | 需要 | 不需要 | 不需要 | +| 返回值 | 自己定义 | 必须是 `void` | 必须有返回值 | +| 参数 | 自己定义 | 可有多个 | 可有多个 | +| 常见用途 | 语义明确的回调 | 简单无返回值操作 | 简单有返回值操作 | + +实际使用建议: +1. 简单无返回值回调,优先考虑 `Action` +2. 简单有返回值回调,优先考虑 `Func` +3. 需要清晰业务语义时,可以定义自己的委托类型 + +---- +# 和事件的关系 +委托: + 可以在类外部赋值 + 也可以在类外部直接调用 + +事件: + 是对委托的进一步封装 + 类外部只能订阅和取消订阅 + 不能直接调用事件 + +可以和[[事件]]一起理解: + 委托负责“存方法和调用方法” + 事件负责“更安全地对外发布通知” + +---- +# 注意 +1. 委托是一种引用类型 +2. 委托变量只能保存签名匹配的方法 +3. 签名主要看参数和返回值 +4. 委托可以绑定一个或多个方法 +5. `+=` 添加方法,`-=` 移除方法 +6. 调用可能为 `null` 的委托前要先判断 +7. `Action` 无返回值,`Func` 有返回值 +8. 自定义委托不能只靠返回值不同来重名 + +---- +# 易错点 +1. 委托不是方法本身,而是“引用方法的变量” +2. 委托类型和方法签名不匹配时,不能赋值 +3. `Add` 和 `+=` 不一样,委托绑定方法用 `+=` +4. 多播委托有返回值时,只能拿到最后一个方法的返回值 +5. `del?.Invoke()` 可以避免空引用异常 + +---- +# 一句话记忆 +==委托就是一种可以存方法、传方法、调方法的类型。== + +---- +# 面试/复习时怎么说 +可以这样答: + +“委托是一种引用方法的类型,可以把符合指定签名的方法赋值给委托变量,再通过委托统一调用。 +它可以作为参数传递,也可以绑定多个方法,常用于回调和事件机制。 +实际开发中,简单场景通常使用系统自带的 `Action` 和 `Func`。” + +---- +引用: +1. Microsoft Learn: [Delegates](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/) +2. Microsoft Learn: [Action Delegate](https://learn.microsoft.com/en-us/dotnet/api/system.action) +3. Microsoft Learn: [Func Delegate](https://learn.microsoft.com/en-us/dotnet/api/system.func-2) + +#委托 +#delegate +#Action +#Func +#进阶 diff --git a/笔记/CSharp笔记汇总.md b/笔记/CSharp笔记汇总.md index ada3e41..b5d2ac4 100644 --- a/笔记/CSharp笔记汇总.md +++ b/笔记/CSharp笔记汇总.md @@ -37,10 +37,10 @@ 5. [[泛型]]和[[泛型约束]] 6. [[List]] 7. [[Dictionary]] -8. 顺序存储和链式存储 +8. [[顺序存储和链式存储]] 9. LinkedList 10. 泛型栈和队列 -11. 委托事件 +11. [[委托]]和[[事件]] 12. 匿名函数 13. lambda 14. List排序 diff --git a/笔记/Linux/基础指令/netstat.md b/笔记/Linux/基础指令/netstat.md index 57040dd..798a329 100644 --- a/笔记/Linux/基础指令/netstat.md +++ b/笔记/Linux/基础指令/netstat.md @@ -1,6 +1,6 @@ # netstat \[-anp] \[| grep 端口号] -**略过时,推荐使用ss(同参数)** +**过时,推荐使用ss(同参数)** 作用:查看端口的占用情况 diff --git a/笔记/Linux/基础指令/wget.md b/笔记/Linux/基础指令/wget.md index b01fc20..c2dc9e0 100644 --- a/笔记/Linux/基础指令/wget.md +++ b/笔记/Linux/基础指令/wget.md @@ -6,4 +6,4 @@ - b:后台下载,会在当前工作目录下写wget-log日志 示例: -- apache- \ No newline at end of file +- \ No newline at end of file diff --git a/笔记/Linux/权限相关/修改权限控制信息.md b/笔记/Linux/权限相关/修改权限控制信息.md index 888bba2..3ab4335 100644 --- a/笔记/Linux/权限相关/修改权限控制信息.md +++ b/笔记/Linux/权限相关/修改权限控制信息.md @@ -1,2 +1,2 @@ -相关指令:chmod +相关指令:[[chmod&chown]] 前提:文件或文件夹的所属用户或root用户 \ No newline at end of file