> 技术文档 > 【Unity每篇一个知识点】C#委托基础详解_unity 委托

【Unity每篇一个知识点】C#委托基础详解_unity 委托


【Unity每篇一个知识点】C#委托基础详解

标签:#Unity #C# #委托基础 #委托定义 #委托类型 #委托实例 #教程

📖 目录

  • 1. 委托基础详解
  • 2. 委托类型详解
  • 3. 委托实例详解
  • 4. 委托应用详解
  • 5. 实际应用
  • 6. 总结

1. 委托基础详解

委托是C#中函数指针的类型安全版本,允许将方法作为参数传递和存储。

1.1 委托基本概念

委托与函数指针对比
特性 函数指针 委托 类型安全 不安全 类型安全 参数检查 无 编译时检查 返回值检查 无 编译时检查 多播支持 不支持 支持多播 内存管理 手动 自动
委托语法结构
语法元素 说明 示例 delegate关键字 声明委托类型 delegate void MyDelegate() 返回类型 委托方法的返回类型 voidintstring 委托名 委托类型的名称 MyDelegateActionFunc 参数列表 委托方法的参数 (int x, string y)

重要知识点:

  • 委托是类型安全的函数指针
  • 委托可以存储方法引用
  • 委托支持多播(多个方法)
  • 委托是引用类型
public class DelegateBasics : MonoBehaviour{ // 基本委托定义 public delegate void SimpleDelegate(); public delegate int CalculateDelegate(int a, int b); public delegate string FormatDelegate(int number); // 委托实例 private SimpleDelegate simpleAction; private CalculateDelegate calculator; private FormatDelegate formatter; void Start() { DemonstrateBasicDelegates(); } void DemonstrateBasicDelegates() { // 委托赋值 simpleAction = SayHello; calculator = Add; formatter = FormatNumber; // 调用委托 simpleAction(); // 输出: Hello! int result = calculator(5, 3); // 结果: 8 string formatted = formatter(42); // 结果: \"Number: 42\" Debug.Log($\"计算结果: {result}\"); Debug.Log($\"格式化结果: {formatted}\"); // 委托可以为null SimpleDelegate nullDelegate = null; if (nullDelegate != null) { nullDelegate(); } } // 委托方法 void SayHello() { Debug.Log(\"Hello!\"); } int Add(int a, int b) { return a + b; } string FormatNumber(int number) { return $\"Number: {number}\"; }}

2. 委托类型详解

C#提供了多种委托类型来满足不同的需求。

2.1 内置委托类型

内置委托对比表
委托类型 语法 说明 使用场景 Action Action 无参数无返回值 简单操作 Action Action 有参数无返回值 带参数操作 Action Action 多参数无返回值 复杂操作 Func Func 无参数有返回值 计算函数 Func Func 有参数有返回值 转换函数 Predicate Predicate 有参数返回bool 条件判断
委托类型特点
特点 说明 示例 类型安全 编译时检查参数和返回值 Action 只能接受 void Method(int) 多播支持 可以绑定多个方法 delegate += method1; delegate += method2; 空值检查 调用前检查是否为null if (delegate != null) delegate(); 链式调用 支持方法链式调用 delegate?.Invoke();

重要知识点:

  • Action系列用于无返回值的方法
  • Func系列用于有返回值的方法
  • Predicate用于返回bool的方法
  • 内置委托减少自定义委托定义
public class DelegateTypes : MonoBehaviour{ void Start() { DemonstrateBuiltInDelegates(); } void DemonstrateBuiltInDelegates() { // Action委托 Action simpleAction = () => Debug.Log(\"简单操作\"); Action<int> numberAction = (x) => Debug.Log($\"数字: {x}\"); Action<int, string> complexAction = (x, y) => Debug.Log($\"{x}: {y}\"); simpleAction(); numberAction(42); complexAction(1, \"测试\"); // Func委托 Func<int> getNumber = () => 42; Func<int, string> formatNumber = (x) => $\"数字: {x}\"; Func<int, int, int> add = (a, b) => a + b; Func<int, int, int, int> multiply = (a, b, c) => a * b * c; Debug.Log($\"获取数字: {getNumber()}\"); Debug.Log($\"格式化: {formatNumber(100)}\"); Debug.Log($\"加法: {add(5, 3)}\"); Debug.Log($\"乘法: {multiply(2, 3, 4)}\"); // Predicate委托 Predicate<int> isEven = (x) => x % 2 == 0; Predicate<string> isEmpty = (s) => string.IsNullOrEmpty(s); Debug.Log($\"8是偶数: {isEven(8)}\"); Debug.Log($\"空字符串: {isEmpty(\"\")}\"); // 使用委托进行数据处理 List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 过滤偶数 List<int> evenNumbers = numbers.FindAll(isEven); Debug.Log($\"偶数: {string.Join(\", \", evenNumbers)}\"); // 转换数字 List<string> formattedNumbers = numbers.ConvertAll(formatNumber); Debug.Log($\"格式化: {string.Join(\", \", formattedNumbers)}\"); // 执行操作 numbers.ForEach(numberAction); }}

2.2 自定义委托类型

自定义委托定义
定义方式 语法 说明 示例 传统委托 delegate void MyDelegate() 完整委托定义 复杂签名 泛型委托 delegate T MyDelegate() 类型参数委托 通用委托 事件委托 delegate void EventHandler() 事件专用委托 事件处理
自定义委托使用场景
场景 说明 示例 复杂签名 内置委托无法满足 多个参数、特殊类型 语义清晰 提高代码可读性 PlayerDeathHandler 事件处理 专门的事件委托 EventHandler 回调函数 异步操作回调 AsyncCallback

重要知识点:

  • 自定义委托提供语义清晰
  • 泛型委托提高代码复用
  • 事件委托专门用于事件
  • 合理选择委托类型
public class CustomDelegates : MonoBehaviour{ // 自定义委托 public delegate void PlayerActionHandler(string playerName, int actionType); public delegate bool GameRuleChecker(int score, int level); public delegate T DataProcessor<T>(T input); public delegate void AsyncCallback(bool success, string result); // 委托实例 private PlayerActionHandler playerAction; private GameRuleChecker ruleChecker; private DataProcessor<int> numberProcessor; private AsyncCallback asyncCallback; void Start() { DemonstrateCustomDelegates(); } void DemonstrateCustomDelegates() { // 玩家动作委托 playerAction = OnPlayerAction; playerAction += OnPlayerActionLog; playerAction(\"Player1\", 1); // 攻击 playerAction(\"Player2\", 2); // 防御 // 游戏规则检查委托 ruleChecker = CheckWinCondition; bool hasWon = ruleChecker(1000, 5); Debug.Log($\"是否获胜: {hasWon}\"); // 泛型数据处理委托 numberProcessor = DoubleNumber; int result = numberProcessor(21); Debug.Log($\"处理结果: {result}\"); // 异步回调委托 asyncCallback = OnAsyncComplete; SimulateAsyncOperation(); } // 委托方法实现 void OnPlayerAction(string playerName, int actionType) { string action = actionType == 1 ? \"攻击\" : \"防御\"; Debug.Log($\"{playerName} 执行 {action}\"); } void OnPlayerActionLog(string playerName, int actionType) { Debug.Log($\"记录: {playerName} 执行动作 {actionType}\"); } bool CheckWinCondition(int score, int level) { return score >= 1000 && level >= 5; } int DoubleNumber(int input) { return input * 2; } void OnAsyncComplete(bool success, string result) { if (success) { Debug.Log($\"异步操作成功: {result}\"); } else { Debug.LogError($\"异步操作失败: {result}\"); } } void SimulateAsyncOperation() { // 模拟异步操作 StartCoroutine(AsyncOperation()); } System.Collections.IEnumerator AsyncOperation() { yield return new WaitForSeconds(1f); bool success = Random.Range(0f, 1f) > 0.3f; string result = success ? \"操作完成\" : \"操作失败\"; asyncCallback?.Invoke(success, result); }}

3. 委托实例详解

委托实例是委托类型的具体实现,可以存储和调用方法。

3.1 委托实例创建

委托实例创建方式
创建方式 语法 说明 示例 方法赋值 delegate = method 直接赋值方法 action = SayHello 构造函数 new DelegateType(method) 使用构造函数 new Action(SayHello) Lambda表达式 delegate = () => {} 匿名方法 action = () => Debug.Log(\"Hello\") 多播委托 delegate += method 添加多个方法 action += method1; action += method2
委托实例操作
操作 语法 说明 示例 调用 delegate() 直接调用 action() 安全调用 delegate?.Invoke() 空值检查调用 action?.Invoke() 添加方法 delegate += method 添加方法到委托 action += SayHello 移除方法 delegate -= method 从委托移除方法 action -= SayHello 清空委托 delegate = null 清空所有方法 action = null

重要知识点:

  • 委托实例可以存储多个方法
  • 委托调用会执行所有绑定的方法
  • 委托支持添加和移除方法
  • 委托可以为null
public class DelegateInstances : MonoBehaviour{ // 委托定义 public delegate void GameEventHandler(string eventName); public delegate int MathOperation(int a, int b); // 委托实例 private GameEventHandler gameEvent; private MathOperation mathOp; void Start() { DemonstrateDelegateInstances(); } void DemonstrateDelegateInstances() { // 创建委托实例 gameEvent = OnGameStart; gameEvent += OnGameEventLog; gameEvent += (eventName) => Debug.Log($\"Lambda: {eventName}\"); // 调用委托 gameEvent(\"GameStart\"); // 移除方法 gameEvent -= OnGameStart; gameEvent(\"GameStart\"); // 只执行剩余的方法 // 数学操作委托 mathOp = Add; Debug.Log($\"加法: {mathOp(5, 3)}\"); mathOp = Multiply; Debug.Log($\"乘法: {mathOp(5, 3)}\"); // 多播数学操作(注意:只有最后一个返回值有效) mathOp = Add; mathOp += Multiply; int result = mathOp(5, 3); // 只返回Multiply的结果 Debug.Log($\"多播结果: {result}\"); // 安全调用 GameEventHandler nullEvent = null; nullEvent?.Invoke(\"Test\"); // 不会抛出异常 // 条件调用 if (nullEvent != null) { nullEvent(\"Test\"); } } // 委托方法 void OnGameStart(string eventName) { Debug.Log($\"游戏开始: {eventName}\"); } void OnGameEventLog(string eventName) { Debug.Log($\"记录事件: {eventName}\"); } int Add(int a, int b) { Debug.Log($\"执行加法: {a} + {b}\"); return a + b; } int Multiply(int a, int b) { Debug.Log($\"执行乘法: {a} * {b}\"); return a * b; }}

3.2 委托实例管理

委托实例生命周期
阶段 说明 操作 创建 实例化委托对象 赋值方法或使用构造函数 绑定 添加方法到委托 使用 += 操作符 调用 执行委托方法 调用委托或使用 Invoke() 解绑 移除方法 使用 -= 操作符 销毁 清空委托 设置为 null
委托实例最佳实践
实践 说明 示例 空值检查 调用前检查是否为null if (delegate != null) delegate(); 安全调用 使用空条件操作符 delegate?.Invoke(); 内存管理 及时移除不需要的方法 delegate -= method; 异常处理 处理委托调用中的异常 try { delegate(); } catch { }

重要知识点:

  • 委托实例需要正确管理生命周期
  • 避免内存泄漏,及时移除方法
  • 使用安全调用避免空引用异常
  • 处理委托调用中的异常
public class DelegateInstanceManagement : MonoBehaviour{ public delegate void PlayerEventHandler(string playerName, int eventType); private PlayerEventHandler playerEvent; private List<PlayerEventHandler> eventHandlers = new List<PlayerEventHandler>(); void Start() { DemonstrateInstanceManagement(); } void DemonstrateInstanceManagement() { // 添加事件处理器 AddEventHandler(OnPlayerDeath); AddEventHandler(OnPlayerLevelUp); AddEventHandler(OnPlayerItemPickup); // 触发事件 TriggerPlayerEvent(\"Player1\", 1); // 死亡 TriggerPlayerEvent(\"Player2\", 2); // 升级 TriggerPlayerEvent(\"Player3\", 3); // 拾取物品 // 移除特定处理器 RemoveEventHandler(OnPlayerDeath); TriggerPlayerEvent(\"Player1\", 1); // 不再执行死亡处理 // 清空所有处理器 ClearAllHandlers(); TriggerPlayerEvent(\"Player1\", 1); // 不会执行任何处理 } void AddEventHandler(PlayerEventHandler handler) { if (handler != null && !eventHandlers.Contains(handler)) { eventHandlers.Add(handler); playerEvent += handler; Debug.Log($\"添加事件处理器: {handler.Method.Name}\"); } } void RemoveEventHandler(PlayerEventHandler handler) { if (handler != null && eventHandlers.Contains(handler)) { eventHandlers.Remove(handler); playerEvent -= handler; Debug.Log($\"移除事件处理器: {handler.Method.Name}\"); } } void ClearAllHandlers() { eventHandlers.Clear(); playerEvent = null; Debug.Log(\"清空所有事件处理器\"); } void TriggerPlayerEvent(string playerName, int eventType) { try { // 安全调用 playerEvent?.Invoke(playerName, eventType); } catch (System.Exception ex) { Debug.LogError($\"事件处理异常: {ex.Message}\"); } } // 事件处理方法 void OnPlayerDeath(string playerName, int eventType) { if (eventType == 1) { Debug.Log($\"玩家 {playerName} 死亡\"); } } void OnPlayerLevelUp(string playerName, int eventType) { if (eventType == 2) { Debug.Log($\"玩家 {playerName} 升级\"); } } void OnPlayerItemPickup(string playerName, int eventType) { if (eventType == 3) { Debug.Log($\"玩家 {playerName} 拾取物品\"); } }}

4. 委托应用详解

委托在实际开发中有广泛的应用场景。

4.1 委托应用场景

委托应用对比表
应用场景 说明 示例 回调函数 异步操作完成通知 网络请求回调 事件处理 响应系统事件 按钮点击事件 策略模式 动态选择算法 排序算法选择 观察者模式 对象间通信 数据变化通知 命令模式 封装操作 撤销/重做操作
委托设计模式
模式 说明 委托作用 回调模式 异步操作完成通知 传递回调函数 观察者模式 一对多通知 事件通知机制 策略模式 算法选择 动态算法切换 命令模式 操作封装 操作执行和撤销

重要知识点:

  • 委托是实现设计模式的重要工具
  • 委托提供松耦合的通信机制
  • 委托支持异步编程模式
  • 委托提高代码的可扩展性
public class DelegateApplications : MonoBehaviour{ void Start() { DemonstrateApplications(); } void DemonstrateApplications() { // 回调函数应用 DemonstrateCallbacks(); // 策略模式应用 DemonstrateStrategyPattern(); // 观察者模式应用 DemonstrateObserverPattern(); // 命令模式应用 DemonstrateCommandPattern(); } // 回调函数应用 void DemonstrateCallbacks() { Debug.Log(\"=== 回调函数应用 ===\"); // 模拟异步操作 StartCoroutine(AsyncOperationWithCallback((success, result) => { if (success) { Debug.Log($\"异步操作成功: {result}\"); } else { Debug.LogError($\"异步操作失败: {result}\"); } })); } System.Collections.IEnumerator AsyncOperationWithCallback(System.Action<bool, string> callback) { yield return new WaitForSeconds(1f); bool success = Random.Range(0f, 1f) > 0.3f; string result = success ? \"数据加载完成\" : \"网络错误\"; callback?.Invoke(success, result); } // 策略模式应用 void DemonstrateStrategyPattern() { Debug.Log(\"=== 策略模式应用 ===\"); List<int> numbers = new List<int> { 3, 1, 4, 1, 5, 9, 2, 6 }; // 不同的排序策略 System.Action<List<int>> bubbleSort = (list) => { for (int i = 0; i < list.Count - 1; i++) { for (int j = 0; j < list.Count - 1 - i; j++) {  if (list[j] > list[j + 1])  { int temp = list[j]; list[j] = list[j + 1]; list[j + 1] = temp;  } } } }; System.Action<List<int>> quickSort = (list) => { list.Sort(); }; // 使用不同策略 List<int> numbers1 = new List<int>(numbers); List<int> numbers2 = new List<int>(numbers); bubbleSort(numbers1); quickSort(numbers2); Debug.Log($\"冒泡排序: {string.Join(\", \", numbers1)}\"); Debug.Log($\"快速排序: {string.Join(\", \", numbers2)}\"); } // 观察者模式应用 void DemonstrateObserverPattern() { Debug.Log(\"=== 观察者模式应用 ===\"); // 数据源 DataSource dataSource = new DataSource(); // 观察者 dataSource.OnDataChanged += (newData) => Debug.Log($\"观察者1收到数据: {newData}\"); dataSource.OnDataChanged += (newData) => Debug.Log($\"观察者2收到数据: {newData}\"); dataSource.OnDataChanged += (newData) => Debug.Log($\"观察者3收到数据: {newData}\"); // 触发数据变化 dataSource.UpdateData(\"新数据1\"); dataSource.UpdateData(\"新数据2\"); } // 命令模式应用 void DemonstrateCommandPattern() { Debug.Log(\"=== 命令模式应用 ===\"); CommandManager commandManager = new CommandManager(); // 添加命令 commandManager.AddCommand(() => Debug.Log(\"执行命令1\")); commandManager.AddCommand(() => Debug.Log(\"执行命令2\")); commandManager.AddCommand(() => Debug.Log(\"执行命令3\")); // 执行所有命令 commandManager.ExecuteAll(); // 撤销命令 commandManager.UndoLast(); }}// 观察者模式 - 数据源public class DataSource{ public System.Action<string> OnDataChanged; public void UpdateData(string newData) { Debug.Log($\"数据源更新: {newData}\"); OnDataChanged?.Invoke(newData); }}// 命令模式 - 命令管理器public class CommandManager{ private List<System.Action> commands = new List<System.Action>(); private List<System.Action> executedCommands = new List<System.Action>(); public void AddCommand(System.Action command) { commands.Add(command); } public void ExecuteAll() { foreach (var command in commands) { command?.Invoke(); executedCommands.Add(command); } commands.Clear(); } public void UndoLast() { if (executedCommands.Count > 0) { var lastCommand = executedCommands[executedCommands.Count - 1]; executedCommands.RemoveAt(executedCommands.Count - 1); Debug.Log(\"撤销最后一个命令\"); } }}

5. 实际应用

5.1 Unity游戏开发中的委托

游戏系统委托应用:

public class UnityDelegateExamples : MonoBehaviour{ // 游戏事件委托 public delegate void GameEventDelegate(string eventName, object data); public delegate void PlayerActionDelegate(string playerName, PlayerAction action); public delegate bool GameRuleDelegate(int score, int level); public delegate void UIUpdateDelegate(string message); // 委托实例 private GameEventDelegate gameEvent; private PlayerActionDelegate playerAction; private GameRuleDelegate gameRule; private UIUpdateDelegate uiUpdate; // 游戏状态 private int currentScore = 0; private int currentLevel = 1; private string currentPlayer = \"Player1\"; void Start() { InitializeDelegates(); TestGameDelegates(); } void InitializeDelegates() { // 初始化游戏事件委托 gameEvent = OnGameEvent; gameEvent += LogGameEvent; // 初始化玩家动作委托 playerAction = OnPlayerAction; playerAction += UpdateUI; // 初始化游戏规则委托 gameRule = CheckWinCondition; // 初始化UI更新委托 uiUpdate = UpdateGameUI; } void TestGameDelegates() { // 触发游戏事件 gameEvent?.Invoke(\"GameStart\", new { Level = currentLevel, Player = currentPlayer }); // 执行玩家动作 playerAction?.Invoke(currentPlayer, PlayerAction.Attack); playerAction?.Invoke(currentPlayer, PlayerAction.Defend); // 检查游戏规则 currentScore = 1000; bool hasWon = gameRule?.Invoke(currentScore, currentLevel) ?? false; if (hasWon) { gameEvent?.Invoke(\"GameWin\", new { Score = currentScore, Level = currentLevel }); } // 更新UI uiUpdate?.Invoke($\"分数: {currentScore}, 等级: {currentLevel}\"); } // 委托方法实现 void OnGameEvent(string eventName, object data) { Debug.Log($\"游戏事件: {eventName}\"); if (data != null) { Debug.Log($\"事件数据: {data}\"); } } void LogGameEvent(string eventName, object data) { Debug.Log($\"[日志] 事件: {eventName}\"); } void OnPlayerAction(string playerName, PlayerAction action) { Debug.Log($\"玩家 {playerName} 执行动作: {action}\"); switch (action) { case PlayerAction.Attack: currentScore += 10; break; case PlayerAction.Defend: currentScore += 5; break; case PlayerAction.Heal: currentScore += 15; break; } } void UpdateUI(string playerName, PlayerAction action) { Debug.Log($\"[UI] 更新玩家 {playerName} 的动作显示: {action}\"); } bool CheckWinCondition(int score, int level) { return score >= 1000 && level >= 5; } void UpdateGameUI(string message) { Debug.Log($\"[UI] {message}\"); } // 公共方法供外部调用 public void TriggerPlayerAction(PlayerAction action) { playerAction?.Invoke(currentPlayer, action); } public void AddGameEventHandler(GameEventDelegate handler) { gameEvent += handler; } public void RemoveGameEventHandler(GameEventDelegate handler) { gameEvent -= handler; }}// 玩家动作枚举public enum PlayerAction{ Attack, Defend, Heal, Move}

6. 总结

6.1 委托类型对比表

委托类型 语法 特点 使用场景 自定义委托 delegate void MyDelegate() 语义清晰 特定业务逻辑 Action委托 ActionAction 无返回值 简单操作 Func委托 FuncFunc 有返回值 计算函数 Predicate委托 Predicate 返回bool 条件判断 事件委托 EventHandler 事件专用 事件处理

6.2 委托操作对比表

操作 语法 说明 示例 创建 delegate = method 赋值方法 action = SayHello 调用 delegate() 直接调用 action() 安全调用 delegate?.Invoke() 空值检查 action?.Invoke() 添加 delegate += method 多播添加 action += method 移除 delegate -= method 多播移除 action -= method 清空 delegate = null 清空所有 action = null

6.3 最佳实践

✅ 推荐做法
  • 使用内置委托类型
  • 进行空值检查
  • 及时移除不需要的方法
  • 使用有意义的委托名
  • 处理委托调用异常
❌ 避免做法
  • 过度使用自定义委托
  • 忽略空值检查
  • 忘记移除方法导致内存泄漏
  • 在委托中进行复杂操作
  • 忽略异常处理

6.4 性能考虑

性能优化策略:

  • 避免在委托中进行复杂计算
  • 合理使用多播委托
  • 及时清理不需要的委托引用
  • 考虑委托调用的性能影响

6.5 实际应用指南

应用场景 推荐委托类型 原因 简单回调 Action 无返回值,简洁 数据转换 Func 有返回值,灵活 条件判断 Predicate 返回bool,语义清晰 事件处理 自定义委托 特定事件,语义明确 异步操作 Action 异步结果通知

C#委托是函数式编程的重要工具,合理使用委托可以提高代码的灵活性和可维护性。

标签:#Unity #C# #委托基础 #委托定义 #委托类型 #委托实例 #教程