You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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));

关键点说明

  1. 委托参数:Func<SwsimV111Soap, TRequest, TResponse> 封装了每个请求对应的SOAP调用逻辑,完美解决不同请求对应不同SOAP方法的问题。
  2. 动态类型适配:因为自动生成的类无法修改,用dynamic来统一访问req.Item和resp.Authenticator属性。如果部分类型没有这些属性,可以添加反射判断逻辑做兼容。
  3. 日志可读性优化:泛型方法的日志源包含了具体的请求/响应类型名称,便于调试时区分不同请求。

内容的提问来源于stack exchange,提问作者Larry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:55:10