C#多对象类型重复逻辑改造:是否适合泛型实现?
泛型重构SOAP请求方法(遵循DRY原则)
问题背景
现有多个GetResponse重载方法,仅在返回类型、传入参数类型、SOAP接口调用语句三处存在差异,当前采用重载设计,希望重构为泛型方法以消除重复代码。注意:所有请求/响应类型均为WSDL自动生成,无法修改。
示例重载方法定义:
private GetAccountInfoResponse GetResponse(GetAccountInfoRequest req) {...} private CleanseAddressResponse GetResponse(CleanseAddressRequest req) {...}
其中一个方法的完整实现(以GetAccountInfo为例):
private GetAccountInfoResponse GetResponse(GetAccountInfoRequest req) { MethodBase m = MethodBase.GetCurrentMethod(); string source = $"{m.ReflectedType.Name}.{m.Name}"; SwsimV111Soap swsim = swsimFactory.CreateChannel(); GetAccountInfoResponse resp = null; int retry = 0; string msg = string.Empty; if (Logging.GetLevelFromString(ConfigSettings.LogLevel) > LogLevels.Information) ReportStatus(LogLevels.Debug, source, $"SWSIM req: {SupportClasses.XmlConvert.SerializeObject(req)}"); while (resp == null && retry < 1) { try { resp = swsim.GetAccountInfo(req); // 这一行是每个重载的核心差异 _authToken = resp.Authenticator; } catch (FaultException ex) { if (ex.Message.ToLower().Contains("converstion out-of-sync")) { ClearAuthState(); req.Item = GetCredsType(); } else { msg = $"Call Fault: {Misc.ParseException(ex)}"; break; } } catch (Exception ex) { msg = $"Call Error: {Misc.ParseException(ex)}"; break; } retry++; } if (resp != null) { if (Logging.GetLevelFromString(ConfigSettings.LogLevel) > LogLevels.Information) ReportStatus(LogLevels.Debug, source, $"SWSIM resp: {SupportClasses.XmlConvert.SerializeObject(resp)}"); } else ReportStatus(LogLevels.Error, source, $"Fault Exception: {msg}"); return resp; }
解决方案:泛型+委托消除重复
核心思路是将变化的SOAP调用逻辑提取为委托参数传入泛型方法,固定的重试、日志、异常处理逻辑完全复用。
泛型方法实现
private TResponse GetResponse<TRequest, TResponse>(TRequest req, Func<SwsimV111Soap, TRequest, TResponse> soapCall) where TRequest : class where TResponse : class { MethodBase m = MethodBase.GetCurrentMethod(); string source = $"{m.ReflectedType.Name}.{m.Name}<{typeof(TRequest).Name}, {typeof(TResponse).Name}>"; SwsimV111Soap swsim = swsimFactory.CreateChannel(); TResponse resp = null; int retry = 0; string msg = string.Empty; if (Logging.GetLevelFromString(ConfigSettings.LogLevel) > LogLevels.Information) ReportStatus(LogLevels.Debug, source, $"SWSIM req: {SupportClasses.XmlConvert.SerializeObject(req)}"); while (resp == null && retry < 1) { try { resp = soapCall(swsim, req); // 执行传入的SOAP调用逻辑 // 处理_authToken:利用动态类型访问自动生成类的Authenticator属性 dynamic dynamicResp = resp; _authToken = dynamicResp.Authenticator; } catch (FaultException ex) { if (ex.Message.ToLower().Contains("converstion out-of-sync")) { ClearAuthState(); // 处理请求凭证:利用动态类型访问自动生成类的Item属性 dynamic dynamicReq = req; dynamicReq.Item = GetCredsType(); } else { msg = $"Call Fault: {Misc.ParseException(ex)}"; break; } } catch (Exception ex) { msg = $"Call Error: {Misc.ParseException(ex)}"; break; } retry++; } if (resp != null) { if (Logging.GetLevelFromString(ConfigSettings.LogLevel) > LogLevels.Information) ReportStatus(LogLevels.Debug, source, $"SWSIM resp: {SupportClasses.XmlConvert.SerializeObject(resp)}"); } else ReportStatus(LogLevels.Error, source, $"Fault Exception: {msg}"); return resp; }
调用方式
替换原来的重载调用,直接传入对应的SOAP方法作为委托:
// 替代原GetAccountInfoResponse重载方法的调用 var accountResp = GetResponse(new GetAccountInfoRequest(), (client, req) => client.GetAccountInfo(req)); // 替代原CleanseAddressResponse重载方法的调用 var addressResp = GetResponse(new CleanseAddressRequest(), (client, req) => client.CleanseAddress(req));
关键点说明
- 委托参数:
Func<SwsimV111Soap, TRequest, TResponse>封装了每个请求对应的SOAP调用逻辑,完美解决不同请求对应不同SOAP方法的问题。 - 动态类型适配:因为自动生成的类无法修改,用
dynamic来统一访问req.Item和resp.Authenticator属性。如果部分类型没有这些属性,可以添加反射判断逻辑做兼容。 - 日志可读性优化:泛型方法的日志源包含了具体的请求/响应类型名称,便于调试时区分不同请求。
内容的提问来源于stack exchange,提问作者Larry
相关产品推荐
相关产品推荐

