C# 同逻辑任务处理方法如何支持返回不同类型结果
解决方案
核心思路是把通用流程和可变逻辑拆分:任务执行、异常捕获、结果合并的固定逻辑留在通用方法里,不同类型的实例创建、属性赋值这类可变逻辑通过委托交给调用方实现,既不需要修改现有三个类型的定义,也完美适配参数化构造函数的要求,全程编译期类型检查,没有反射的性能和稳定性问题。
不要尝试用new()泛型约束,该约束仅支持无参构造函数,无法适配Type1、Type2的参数化构造要求;也不要用反射动态调用构造函数,既存在性能损耗,构造参数变更时也无法在编译期捕获错误,容易引发运行时异常。
通用泛型方法实现
private async Task<T> GetPoliciesFromTasks<T>( IList<Task> taskList, Func<T> defaultInstanceFactory, Action<T, AppsPermissionSettings> permissionPolicySetter) { TeamsAppPermissionPolicy backendPermissionDoc = new TeamsAppPermissionPolicy(); try { await Task.WhenAll(taskList); } catch (Exception ex) { foreach (Task faultedTask in taskList.Where(t => t.IsFaulted)) { Exception innerEx = faultedTask.Exception?.InnerException; DiagnosticsContext.Logger.LogError($"Task {faultedTask} execution failed, exception: {ex}"); } } // 读取主任务结果,失败时通过传入的工厂创建默认实例 T result = taskList[0].IsFaulted ? defaultInstanceFactory() : ((Task<T>)taskList[0]).Result; // 读取权限任务结果 if (taskList.Count > 1) { backendPermissionDoc = taskList[1].IsFaulted ? new TeamsAppPermissionPolicy() : ((Task<TeamsAppPermissionPolicy>)taskList[1]).Result; } // 合并权限结果,通过传入的委托赋值到对应属性 AppsPermissionSettings mergedPolicy = backendPermissionDoc .ToMTTeamsAppPermissionPolicy() .ToMTPolicyDocument(); permissionPolicySetter(result, mergedPolicy); return result; }
各场景调用示例
1. 返回AcmsSettings类型
任务构造逻辑和原有逻辑完全一致,调用时传入对应工厂和赋值逻辑即可:
var taskList = new List<Task> { _myProxy.GetMySettings(/* 原有业务参数 */) }; if (isTenantResolutionModelCustom) { taskList.Add(GetDocFromCacheOrBackendAsync(null, userId)); } AcmsSettings settingsResult = await GetPoliciesFromTasks( taskList, defaultInstanceFactory: () => new AcmsSettings(), permissionPolicySetter: (instance, policy) => instance.AppsPermissionPolicy = policy );
2. 返回Type1类型
提前准备好Type1构造所需的参数,在工厂委托中传入即可,通用方法完全不需要感知构造逻辑:
var taskList = new List<Task> { _myProxy.GetType1Settings(tenantId, userPoliciesToFetch) // 该方法返回Task<Type1> }; if (isTenantResolutionModelCustom) { taskList.Add(GetDocFromCacheOrBackendAsync(null, userId)); } // 提前准备构造参数 UserSettings userSettings = GetCurrentUserSettings(); Permissions basePermissions = GetUserBasePermissions(); Type1 type1Result = await GetPoliciesFromTasks( taskList, defaultInstanceFactory: () => new Type1(userSettings, basePermissions), permissionPolicySetter: (instance, policy) => instance.AppsPermissionPolicy = policy );
3. 返回Type2类型
调用逻辑和Type1完全一致,传入对应构造参数即可:
var taskList = new List<Task> { _myProxy.GetType2Settings(/* Type2对应方法的业务参数 */) // 该方法返回Task<Type2> }; if (isTenantResolutionModelCustom) { taskList.Add(GetDocFromCacheOrBackendAsync(null, userId)); } // 提前准备构造参数 Properties type2Props = GetType2Properties(); Permissions basePermissions = GetUserBasePermissions(); Type2 type2Result = await GetPoliciesFromTasks( taskList, defaultInstanceFactory: () => new Type2(type2Props, basePermissions), permissionPolicySetter: (instance, policy) => instance.AppsPermissionPolicy = policy );
可选简化方案
如果可以修改现有三个类型的定义,可以给所有带AppsPermissionPolicy属性的类型统一加一个公共接口,省掉每次传permissionPolicySetter的步骤:
// 定义公共接口 public interface IPermissionPolicyContainer { AppsPermissionSettings AppsPermissionPolicy { get; set; } } // 三个类型分别实现该接口,不需要修改原有类的任何业务代码 public class AcmsSettings : IPermissionPolicyContainer { /* 原有代码保持不变 */ } public class Type1 : IPermissionPolicyContainer { /* 原有代码保持不变 */ } public class Type2 : IPermissionPolicyContainer { /* 原有代码保持不变 */ }
给泛型方法加接口约束后,就可以去掉赋值委托参数,方法内部直接完成属性赋值:
private async Task<T> GetPoliciesFromTasks<T>( IList<Task> taskList, Func<T> defaultInstanceFactory) where T : IPermissionPolicyContainer { // 其余逻辑和之前一致,赋值时直接操作接口属性即可 result.AppsPermissionPolicy = mergedPolicy; return result; }
后续如果新增同结构的类型,只要实现这个接口就能直接复用方法,不需要调整通用逻辑。
内容的提问来源于stack exchange,提问作者Programmerzzz
相关产品推荐
相关产品推荐

