如何在C#中用委托参数泛化异步方法以减少冗余代码?
异步重试逻辑的泛化优化方案
问题描述
我有若干返回Task<bool>的异步方法,需要在它们返回true或达到最大尝试次数前,多次调用并在每次调用间添加延迟。目前已实现需求,但存在代码冗余。
尝试过通过传递委托泛化逻辑,但最初传递Task只会执行一次,后来写了多个不同Func签名的重载方法,还是没法合并成单一方法。
待泛化的重复代码示例:
for (int attempts = 0; attempts < numOfTaskAttempts; ++attempts) { if (!await UpdateSingleUserStatistic(sendingPlayer, PlayfabServerKeys.DailyPuzzle, request.score)) await Task.Delay(taskAttemptDelay); else break; }
已实现的重载方法示例(仅展示一个):
private static async Task<bool> CallAsyncMethodWithRetries( Func<Task<bool>> callback, int maxAttempts = numOfTaskAttempts, int delay = taskAttemptDelay) { int numOfTries = 0; while (!await callback() && ++numOfTries < maxAttempts) await Task.Delay(delay); return numOfTries < maxAttempts; } // 其他两个重载方法省略...
当前调用方式:
bool isSuccess = false; if (await CallAsyncMethodWithRetries(CreateAndSetTopPuzzlePlayers)) if (await CallAsyncMethodWithRetries(SetTitleData, PlayfabServerKeys.DailyPuzzle, dailyPuzzle)) isSuccess = await CallAsyncMethodWithRetries(ResetLeaderboard, PlayfabServerKeys.DailyPuzzle);
作为异步编程新手,希望找到进一步减少冗余的方案。
优化方案:用闭包消除重载冗余
不需要维护多个重载方法,利用闭包就能把所有带参数的异步方法统一包装成无参数的Func<Task<bool>>,复用同一个核心重试逻辑。
1. 保留单一核心重试方法
只需要维护这一个通用方法即可,逻辑更直观,避免原版本的自增陷阱:
private static async Task<bool> CallWithRetries(Func<Task<bool>> asyncAction, int maxAttempts = numOfTaskAttempts, int delayMs = taskAttemptDelay) { for (int attempt = 0; attempt < maxAttempts; attempt++) { if (await asyncAction()) return true; // 最后一次尝试失败后无需延迟 if (attempt < maxAttempts - 1) await Task.Delay(delayMs); } return false; }
2. 调用时用Lambda包装带参数方法
对于带参数的异步方法,直接用Lambda表达式捕获参数,包装成无参数委托:
// 无参数方法直接传递 bool step1 = await CallWithRetries(CreateAndSetTopPuzzlePlayers); // 带参数方法用Lambda包装 bool step2 = await CallWithRetries(() => SetTitleData(PlayfabServerKeys.DailyPuzzle, dailyPuzzle)); bool step3 = await CallWithRetries(() => UpdateSingleUserStatistic(sendingPlayer, PlayfabServerKeys.DailyPuzzle, request.score));
3. 简化链式调用写法
如果是依赖前序成功的链式调用,利用C#短路逻辑可以简化成一行:
bool isSuccess = await CallWithRetries(CreateAndSetTopPuzzlePlayers) && await CallWithRetries(() => SetTitleData(PlayfabServerKeys.DailyPuzzle, dailyPuzzle)) && await CallWithRetries(() => ResetLeaderboard(PlayfabServerKeys.DailyPuzzle));
前面的方法失败后,后面的会自动跳过,和嵌套if逻辑完全一致。
可选扩展:自定义重试条件
如果需要更灵活的重试规则(比如根据特定结果或异常判断),可以扩展方法增加重试条件委托:
private static async Task<bool> CallWithRetries(Func<Task<bool>> asyncAction, Func<bool, bool> shouldRetry = null, int maxAttempts = numOfTaskAttempts, int delayMs = taskAttemptDelay) { // 默认规则:返回false则重试 shouldRetry ??= result => !result; for (int attempt = 0; attempt < maxAttempts; attempt++) { bool result = await asyncAction(); if (!shouldRetry(result)) return result; if (attempt < maxAttempts - 1) await Task.Delay(delayMs); } return await asyncAction(); }
内容的提问来源于stack exchange,提问作者Samhain323
相关产品推荐
相关产品推荐

