深入理解C#委托操作:添加、移除与调用全解析

发布于:2025-06-29 ⋅ 阅读:(12) ⋅ 点赞:(0)

关键词:委托不可变性 · 多播委托 · 调用列表管理

⚙️ 一、委托的核心特性:不可变性

看似“添加”,实为新建

使用 += 为委托“添加”方法时(如 delVar += SCl.m3;):

  • 系统创建全新委托对象
  • 新委托的调用列表 = 原列表 + 新增方法
  • 原委托对象保持不变(内存地址不变)

✅ 本质:通过新建实现“修改”,符合委托不可变原则

内存变化图解

MyDel delVar = inst.MyM1;  // 初始委托 (指向方法1)
delVar += SCl.m3;          // 新建委托 (方法1+方法2)
delVar += X.Act;           // 再新建委托 (方法1+2+3)

🧩 二、安全移除方法的机制

-= 运算符的运作逻辑

delVar -= SCl.m3;  // 从调用列表移除方法
  • 创建新委托对象,复制除目标方法外的所有引用
  • 移除规则:
    • 从列表尾部向前搜索
    • 仅移除第一个匹配的方法实例
    • 试图移除不存在的方法时静默忽略

空委托防护

// 正确检查方式
if (delVar != null) 
{
    delVar(55); 
}
// 或使用空条件运算符 
delVar?.Invoke(65);

⚠️ 未检查空委托直接调用将抛出 NullReferenceException

📡 三、委托调用的双模式与陷阱

image

关键特性:

  • 参数传递:调用时传入的参数应用于所有方法
  • 重复执行:若同一方法在列表中出现多次,每次都会被调用
  • 输出参数:需特殊处理(避免值覆盖问题)

💻 四、完整实例解析

delegate void PrintFunction();  // 无返回值委托 
 
class Test
{
    public void Print1() => Console.WriteLine("Print1 -- instance");
    public static void Print2() => Console.WriteLine("Print2 -- static");
}
 
class Program
{
    static void Main()
    {
        var t = new Test();
        PrintFunction pf = t.Print1;  // 初始化
        
        // 添加三个方法(实际新建两次委托)
        pf += Test.Print2;  
        pf += t.Print1;     
        pf += Test.Print2;
 
        pf?.Invoke();  // 安全调用 
    }
}

输出结果:

Print1 – instance
Print2 – static
Print1 – instance
Print2 – static

🔑 关键点总结

  • 不可变性是核心:所有“修改”操作实质是创建新委托对象
  • 移除顺序敏感:从列表尾部开始匹配移除首个目标方法
  • 空委托防护:必须使用 if (delVar != null) 或 ?. 运算符
  • 调用成本:每次调用遍历整个调用列表,需注意性能影响

掌握委托的不可变本质,能有效避免异步编程中的常见陷阱。建议在事件处理等场景中始终遵循「添加后必移除」原则,防止内存泄漏问题。


网站公告

今日签到

点亮在社区的每一天
去签到