C# Delegate.Target/ Method

📅 2026/7/21 9:23:02
C# Delegate.Target/ Method
c 详解配合前面 Delegate.Target/Method、Lambda 闭包c Roslyn (csc 编译器) 自动生成的内部密封类源码不存在反编译 ILSpy 可见专门承载 Lambda / 匿名方法。 命名规则c前缀是编译器约定符号C# 标识符不允许避免和用户类重名。一、两种形态c / c__DisplayClassX_X1. 最简c无捕获局部变量的 Lambda → 静态委托缓存Lambda不抓任何外部局部变量编译器生成静态内部类 c把 lambda 编译成静态方法委托复用同一个静态实例优化避免反复 new 委托Microsoft Developer Blogs。// 无捕获变量 Action act () Console.WriteLine(test);编译后[CompilerGenerated] private sealed class c { // 缓存委托实例全局单例 public static readonly c 9 new c(); public Action 9__0_0; // Lambda被编译成静态方法9__0_0 internal void Mainb__0_0() { Console.WriteLine(test); } } // 委托直接绑定静态方法Targetnull静态方法 act c.9.9__0_0 ?? (c.9.9__0_0 new Action(c.9.Mainb__0_0)); 此时委托Target null、Method Mainb__0_0对应你之前看的 Delegate 两个属性。2. 带捕获c__DisplayClass0_0闭包抓取外部局部变量Lambda 用到方法内局部变量编译器生成闭包类 DisplayClass被捕获的局部变量 → 变成这个类的public 实例字段栈变量升级堆字段避免方法出栈被销毁Lambda 代码 → 编译成此类的实例方法Mainb__0new 一个 DisplayClass 对象委托绑定实例方法 →委托.Target DisplayClass 实例委托强引用这个对象闭包内存泄漏根源int num 10; Action act () Console.WriteLine(num);编译等价手写[CompilerGenerated] private sealed class c__DisplayClass0_0 { public int num; // 捕获变量升为字段 internal void Mainb__0_0() { Console.WriteLine(num); } } // 创建闭包对象 var cls new c__DisplayClass0_0(); cls.num num; // 委托绑定实例方法 → TargetclsMethodb__0_0 Action act new Action(cls.Mainb__0_0);二、命名分段释义c__DisplayClass0_0编译器专属标识C# 语法不能自定义以此开头的类名防冲突cclass 缩写代表 Lambda / 匿名方法生成类DisplayClass存储捕获变量的闭包展示类0_0外层方法序号_当前 lambda 序号补充其他编译器生成命名Maind__1async/await异步状态机类XXXb__0lambda 对应的编译后方法名三、和 Delegate.Target/ Method 联动重点衔接上文表格Lambda 场景生成类Delegate.TargetDelegate.Method无捕获局部变量c静态类nullc.Mainb__xxx捕获局部变量c__DisplayClassDisplayClass 实例对象DisplayClass.Mainb__xxx关键只要 Target 不为 null委托就强引用 DisplayClass 实例实例无法 GC → 闭包内存泄漏也就是你之前问WeakReference的使用场景用弱引用包裹 Target打破强引用链。四、拓展顶级语句 Top-level ProgramC#9 顶级语句整个代码包在自动生成的Program类内部里面的 lambda 同样生成c/DisplayClass。五、快速区分记忆不用外部变量 → c静态、缓存委托、Targetnull用外部局部变量 → c__DisplayClass实例类、存字段、Target 实例对象你看到的 1 到底是什么超级简单版你在看泛型类型反编译 / 反射时一定会看到类似Action1、Func2、Dictionary2这个1是CLR 运行时的泛型编号语法不是 C# 语法1. 一句话解释n 这个泛型类型有 n 个泛型参数1 → 1 个泛型参数2 → 2 个泛型参数3 → 3 个泛型参数2. 对应关系一看就懂表格C# 写法CLR 内部名称含义ActionTAction11 个泛型参数ActionT1,T2Action22 个泛型参数FuncTResultFunc11 个泛型参数FuncT1,TResultFunc22 个泛型参数ListTList11 个泛型参数DictionaryTKey,TValueDictionary22 个泛型参数3. 结合你刚才问的内容你之前问typeof(Action)它的FullName就是System.Action1意思Action1 有 1 个泛型参数的泛型委托模板4. 为什么要用 1 这种奇怪符号因为 CLR 要区分Action无参ActionT1 参ActionT1,T22 参它们名字都叫Action所以 CLR 用1、2来区分。5. 超级总结3 秒记住1 1 个泛型参数2 2 个泛型参数它是CLR 内部名称不是 C# 代码typeof(Action)→ 显示Action1如果你愿意我可以把你前面问的WeakReference Delegate Target/Method c 1C# WeakReference 详解WeakReference弱引用是 C# 中允许垃圾回收器 (GC) 回收对象同时又能临时访问该对象的特殊引用类型。它和我们平时用的强引用直接赋值var obj new MyClass()核心区别强引用只要引用存在GC永远不会回收对象弱引用GC 可以随时回收对象你只是保留了一个 访问权限一、核心概念弱引用不阻止垃圾回收即使你持有WeakReferenceGC 判定对象无强引用时会直接回收该对象内存。使用前必须检查访问弱引用对象前必须判断对象是否还存活未被回收。适用场景缓存、非关键数据、大对象临时持有不希望占用内存不释放。二、基础用法1. 创建弱引用// 1. 创建一个强引用对象 var myObj new MyClass { Name 测试对象 }; // 2. 创建弱引用指向 myObj WeakReference weakRef new WeakReference(myObj); // 3. 释放强引用让 GC 可以回收对象 myObj null;2. 安全获取弱引用对象必须判空// 正确写法先获取再判断是否为 null if (weakRef.Target is MyClass obj obj ! null) { // 对象存活可以使用 Console.WriteLine(obj.Name); } else { // 对象已被GC回收 Console.WriteLine(对象已被销毁); }3. 手动触发 GC 测试回收效果using System; class MyClass { public string Name { get; set; } } class Program { static void Main() { var myObj new MyClass { Name 弱引用测试 }; WeakReference weakRef new WeakReference(myObj); // 释放强引用 myObj null; // 强制GC回收 GC.Collect(); GC.WaitForPendingFinalizers(); // 检查对象 if (weakRef.Target is MyClass obj) Console.WriteLine(对象存活 obj.Name); else Console.WriteLine(对象已被GC回收); } }输出对象已被GC回收三、重要属性 方法表格成员作用Target获取 / 设置弱引用指向的对象使用前必须判空IsAlive判断对象是否未被回收不推荐直接用⚠️ 不推荐直接用IsAlive// 错误写法 if (weakRef.IsAlive) { // 这里可能已经被GC回收了存在竞态条件 var obj (MyClass)weakRef.Target; }原因判断IsAlive后、访问Target前GC 可能刚好回收对象。正确做法直接获取Target并判空。四、进阶WeakReferenceT 泛型版本C# 4.0 引入了泛型弱引用类型安全、无需强制转换推荐优先使用。// 泛型弱引用推荐 WeakReferenceMyClass weakRef new WeakReferenceMyClass(myObj); // 安全获取 if (weakRef.TryGetTarget(out MyClass obj)) { // 使用 obj }TryGetTarget是原子操作线程安全无竞态问题。五、典型使用场景内存敏感型缓存缓存图片、数据模型内存不足时让 GC 自动清理不影响程序稳定性。事件订阅避免内存泄漏弱引用事件防止长生命周期对象持有短生命周期对象导致无法回收。大对象临时缓存不希望大对象常驻内存又想在未回收时复用。六、注意事项不要用弱引用持有短时间、高频使用的对象性能不如强引用。值类型不能用弱引用值类型在栈上GC 不管理。弱引用对象不能依赖它存活随时可能为 null。优先使用WeakReferenceT而非非泛型WeakReference。总结WeakReference不阻止 GC 回收的引用核心用法获取 Target 判空 / TryGetTarget优先用WeakReferenceT类型安全、线程安全适用缓存、非关键数据、大对象临时持有禁忌依赖弱引用对象必须存在、高频小对象使用C# Delegate委托详解Delegate委托是 C# 中的类型安全的函数指针本质是一个存储方法引用的类。 简单说委托就是 “可以把方法当成变量 / 参数 / 返回值来传递” 的工具。一、核心概念委托是一个类用来封装方法静态 / 实例方法都可以委托定义了方法的签名返回值 参数符合签名的方法都能被它引用用途回调、事件、Lambda、匿名方法、多播全靠委托实现二、最基础用法4 步1. 声明委托类型// 定义无参数、无返回值 public delegate void MyDelegate(); // 定义带 int 参数、返回 string public delegate string CalcDelegate(int a, int b);2. 写一个匹配签名的方法void SayHi() { Console.WriteLine(你好委托); } string Add(int x, int y) { return (x y).ToString(); }3. 创建委托实例绑定方法MyDelegate del SayHi; // 绑定方法 CalcDelegate calc Add;4. 调用委托等于调用方法del(); // 输出你好委托 string res calc(10, 20); // 30三、高级用法委托作为方法参数回调这是委托最常用的场景把方法传给另一个方法执行。// 接收委托作为参数 void Execute(MyDelegate action) { Console.WriteLine(执行前...); action(); // 调用传入的方法 Console.WriteLine(执行后...); } // 使用 Execute(SayHi);输出执行前... 你好委托 执行后...四、多播委托一个委托调用多个方法委托可以用绑定多个方法一次性调用全部。MyDelegate multiDel Method1; multiDel Method2; // 追加方法 multiDel(); // 一次调用执行两个方法五、系统内置委托99% 场景不用自己定义C# 提供了 3 个万能委托开发中直接用不用手写 delegate1.Action无返回值Action无参无返回ActionT1 个参数无返回ActionT1,T22 个参数无返回Actionstring showMsg Console.WriteLine; showMsg(测试);2.Func有返回值最后一个泛型是返回值Funcint, int, int add (a, b) a b; int sum add(2, 3); // 53.Predicate返回 bool判断用Predicateint isPositive n n 0; bool res isPositive(10); // true六、委托 Lambda最常用写法实际开发中几乎不单独写方法直接用 Lambda// 一行完成定义 绑定 调用 Funcint, int square x x * x; int result square(5); // 25七、委托 vs 接口简单区别委托专注方法传递、回调、事件接口专注一组功能规范简单记只传一个方法用委托一组方法用接口八、关键要点总结委托 类型安全的函数指针作用把方法当参数传递回调核心优先用系统内置Action/Func支持多播一个委托调多个方法是事件event的基础一句话记住委托委托就是让方法可以像变量一样被传来传去。彻底搞懂 Delegate 的Method和Target属性你贴的这两个属性是委托最核心的两个信息我用最简单、最直白的方式给你讲透一看就懂。一句话总结Method委托指向哪个方法方法的信息名字、参数、私有 / 公有Target这个方法属于哪个对象实例对象 /null 静态方法1.Target属性委托属于哪个对象规则实例方法→Target调用该方法的对象本身静态方法→Targetnull示例class MyClass { // 实例方法 public void Show() { } // 静态方法 public static void StaticShow() { } }MyClass obj new MyClass(); // 委托绑定 实例方法 Action action1 obj.Show; Console.WriteLine(action1.Target); // 输出MyClass 对象不为null // 委托绑定 静态方法 Action action2 MyClass.StaticShow; Console.WriteLine(action2.Target); // 输出null2.Method属性委托指向哪个方法返回一个MethodInfo包含方法的所有信息方法名返回值参数列表访问权限public/private是否静态示例Action action obj.Show; MethodInfo method action.Method; Console.WriteLine(method.Name); // Show Console.WriteLine(method.IsStatic); // False Console.WriteLine(method.IsPublic); // True3. 最直观对比表表格委托绑定的方法TargetMethod实例方法方法所属的对象方法信息静态方法null方法信息4. 为什么这两个属性很重要用途 1判断两个委托是否指向同一个方法if (del1.Target del2.Target del1.Method del2.Method) { // 两个委托完全一样 }用途 2反射动态调用你可以通过TargetMethod手动调用方法action.Method.Invoke(action.Target, null);用途 3事件 / 委托调试、日志记录记录当前委托执行的是哪个对象的哪个方法。5. 结合你之前问的 WeakReferenceTarget你刚才问过WeakReference这里有个超级关键的知识点✅ 委托的 Target 就是强引用MyClass obj new MyClass(); Action action obj.Show;只要action不释放obj 永远不会被 GC 回收这就是委托导致内存泄漏的根本原因。总结超级好记Target 方法属于哪个对象实例方法有值静态方法为 nullMethod 方法本身的信息叫什么、参数、权限typeof(ActionT)到底是什么我用最直白、最核心的方式给你讲清楚结合你刚才看的Delegate.Target/Method一次性打通1. 先看本质typeof(ActionT)它 泛型委托ActionT的「类型对象」Type 实例不是对象不是方法是「类型本身」的元数据。2. 和普通Action、Actionint的区别表格写法含义typeof(Action)非泛型、无参委托类型typeof(Actionint)构造好的泛型委托int 版typeof(ActionT)未绑定的泛型定义开放泛型重点ActionT是开放泛型open genericActionint是封闭泛型closed generic你写typeof(ActionT)得到的是System.Action1[T]1 表示这是有 1 个泛型参数的泛型类型定义。3. 代码一看就懂// 泛型定义开放泛型 Type openType typeof(Action); // 注意Action 等价 ActionT Console.WriteLine(openType); // 输出System.Action1[T] // 封闭泛型已指定类型 Type closedType typeof(Actionint); Console.WriteLine(closedType); // 输出System.Action1[System.Int32]4. 它和你刚才看的 Target/Method 有什么关系关系非常大委托本质结构Delegate ├─ Target对象 ├─ Method方法 └─ MethodPtr函数指针而typeof(ActionT)是这个委托属于哪个「类型」5. 最实用的用途你以后一定会用到用途 1判断一个对象是不是 ActionT 委托bool isGenericAction del.GetType().GetGenericTypeDefinition() typeof(Action);用途 2反射创建泛型委托Type genericType typeof(Action); Type constructedType genericType.MakeGenericType(typeof(string)); // 动态创建 Actionstring Delegate action Delegate.CreateDelegate(constructedType, ...);用途 3获取泛型参数类型Type[] args typeof(Actionint).GetGenericArguments(); Type t args[0]; // int6. 超级总结3 秒记住typeof(ActionT) 泛型委托的「原型 / 模板」它是开放泛型类型定义必须MakeGenericType后才能真正使用和Target/Method关系Target是实例typeof是类型一句话终极记忆typeof(Action)是委托的「模具」Actionint是用模具造出来的「成品」。