C#知识学习-022(上下文关键字_1 ) 目录1.add1.1 什么是事件访问器add / remove1.2 为什么需要自定义 add / remove2.extensionc#142.1 基本结构2.2 使用2.2.1 扩展属性2.2.2 扩展运算符2.2.3 扩展索引器c#152.2.4 静态扩展成员3.get3.1基本含义3.2 fieldc#141.add1.1 什么是事件访问器add / remove在 C# 里事件本质上是一对访问器方法add当外部代码使用订阅事件时调用remove当外部代码使用-取消订阅事件时调用编译器默认会为你生成这两个访问器背后通常用Delegate.Combine/Delegate.Remove来管理委托链。但你可以自己定义它们这就是“自定义事件访问器”。通俗的解释一下想象一个场景有个广播站你的类很多人拿着遥控器订阅者广播站一喊“开工啦” 所有遥控器都会收到信号执行自己的动作事件就是广播站​ 就是有人拿遥控器连上了广播站​- 就是拔掉遥控器1.2 为什么需要自定义 add / remove大多数情况下你不需要自己写直接用public event EventHandler OnDraw;以下示例显示了具有自定义add和 remove 访问器的事件⚠️规则一旦你提供了自定义的add就必须同时提供remove否则编译不通过。class Events : IDrawingObject { //这是一个普通的事件;通常是 private只在类内部使用;用来真正存储委托链 event EventHandler PreDrawEvent; //这是显式实现的接口事件;外部只能通过接口引用访问;类本身不能直接 OnDraw ... event EventHandler IDrawingObject.OnDraw { //自定义 add / remove // 有人连 OnDraw其实连到 PreDrawEvent add PreDrawEvent value; remove PreDrawEvent - value; } }为什么要加一层用OnDraw呢符合接口要求必须接口说要有你就得有。控制权在你手里只有你能Invoke外面只能订阅 / 取消订阅触发时机由你决定// 只有我能这么干 PreDrawEvent?.Invoke(this, EventArgs.Empty);当外部代码写 IDrawingObject obj new Events(); obj.OnDraw Handler; 实际执行的是 PreDrawEvent Handler; 这相当于把接口事件“转发”到了内部事件。2.extensionc#142.1 基本结构public static class NumericSequences { extension(IEnumerableint sequence) { public IEnumerableint AddValue(int operand) { foreach (var item in sequence) yield return item operand; } } }解释你有一串数字1, 2, 3, 4, 5。现在你想给这串数字的每一个都加一个相同的数extension(IEnumerableint sequence)我要给“一串整数”挂功能这串整数我在里面叫它sequencepublic IEnumerableint AddValue(int operand)​​​​​​​这是一个对外可见的功能需要一个参数operand要加的那个数用的时候发生了什么numbers numbers.AddValue(10);原来1 2 3 4 5现在11 12 13 14 15所以看起来就像是“numbers自己加了 10。”2.2 使用2.2.1 扩展属性extension(IEnumerableint sequence) { //算一个“中位数” public int Median { //get { ... }表示每次你去读 Median里面的代码才会跑 get { //先把这串数字从小到大排队 var sorted sequence.OrderBy(x x).ToList(); //找到中间的位置 int mid sorted.Count / 2; //偶数个 vs 奇数个 return sorted.Count % 2 0 ? (sorted[mid - 1] sorted[mid]) / 2 : sorted[mid]; } } } 使用var median numbers.Median;2.2.2 扩展运算符public static IEnumerableint operator ( IEnumerableint left, IEnumerableint right) //把右边这串接到左边这串后面 left.Concat(right); 使用var combined numbers Enumerable.Range(100, 10);2.2.3 扩展索引器c#15extension(IEnumerableint sequence) { //像数组一样用 [ ]去序列里拿第 index 个元素 public int this[int index] sequence.ElementAt(index); } 使用int third numbers[2];2.2.4 静态扩展成员如果只写静态成员extension里可以不给接收方起名字extension(IEnumerableint) { public static IEnumerableint Generate(int start, int count, int step) { for (int i 0; i count; i) yield return start i * step; } public static IEnumerableint Identity Enumerable.Emptyint(); } 使用方式像静态成员 //5, 7, 9, 11, 13, 15, 17, 19, 21, 23 var seq IEnumerableint.Generate(5, 10, 2); var id IEnumerableint.Identity;3.get3.1基本含义get 就是“读属性的值”用的set 就是“写属性的值”用的属性property​ 可以理解成给字段穿的一层“安全外套”。先看最原始的样子class TimePeriod { private double _seconds; // 真正存数据的地方字段 public double Seconds { get { return _seconds; // 读值 } set { if (value 0) throw new ArgumentOutOfRangeException(不能小于0); _seconds value; // 写值 } } }Seconds就是对外暴露的属性_seconds是真正存数据的“仓库”使用示例 var t new TimePeriod(); t.Seconds 10; // 调用 set double x t.Seconds; // 调用 get属性本质是方法属性 字段 控制逻辑你可以拦截、校验、打日志、触发事件如果不需要校验C# 允许你这么写public double Hours { get; set; }编译器偷偷帮你生成了private double _hours; public double Hours { get { return _hours; } set { _hours value; } }3.2 fieldc#14想半自动实现属性public double Hours { get; set field (value 0) ? value : throw new ArgumentOutOfRangeException(); }field 是编译器自动生成的那个私有字段你不用再自己写_hours只手写setget 自动生成还能校验学到了这里咱俩真棒记得按时吃饭我和烤肉之间就差一撮孜然【本篇结束新的知识会不定时补充】感谢你的阅读如果内容有帮助欢迎 ​​点赞❤️ 收藏⭐ 关注​​ 支持