【Unity每篇一个知识点】C#委托基础详解_unity 委托
【Unity每篇一个知识点】C#委托基础详解
标签:#Unity #C# #委托基础 #委托定义 #委托类型 #委托实例 #教程
📖 目录
- 1. 委托基础详解
- 2. 委托类型详解
- 3. 委托实例详解
- 4. 委托应用详解
- 5. 实际应用
- 6. 总结
1. 委托基础详解
委托是C#中函数指针的类型安全版本,允许将方法作为参数传递和存储。
1.1 委托基本概念
委托与函数指针对比
委托语法结构
delegate void MyDelegate()
void
、int
、string
MyDelegate
、Action
、Func
(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
Func
Func
Predicate
委托类型特点
Action
只能接受 void Method(int)
delegate += method1; delegate += method2;
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)
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
委托实例最佳实践
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
、Action
Func
、Func
Predicate
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 实际应用指南
C#委托是函数式编程的重要工具,合理使用委托可以提高代码的灵活性和可维护性。
标签:#Unity #C# #委托基础 #委托定义 #委托类型 #委托实例 #教程