C#中如何用var兼容Type1、Type2两种自动生成类返回值
问题背景
- 现有两个自动生成的类
Type1、Type2,二者各自关联其他自动生成的依赖类,由于是工具自动生成的代码,无法直接修改类定义让它们继承公共抽象类或实现统一接口。 - 业务中存在两个服务方法,分别返回
Type1和Type2类型的结果,两个类的公开属性结构几乎完全一致。最初尝试用统一变量接收两个方法的返回值、复用后续赋值逻辑,但受C#编译时强类型校验限制,写法无法通过编译:
// 错误写法示例1:三元运算符无法直接返回类型作为变量声明 var response = string.IsNullOrWhiteSpace(inputObject.HistoricYear) ? Type1: Type2; if (string.IsNullOrWhiteSpace(inputObject.HistoricYear)) { response = TsiService.GetAccountTransactionsList(inputObject); } else { response = TsiService.GetAccountTransactionsHist(inputObject); }
- 后续尝试用
object类型或者无类型变量接收返回值,编译时提示找不到成员定义的错误:
// 错误写法示例2:object类型没有对应属性定义,编译不通过 var response = null; if (string.IsNullOrWhiteSpace(inputObject.HistoricYear)) { response = TsiService.GetAccountTransactionsList(inputObject); } else { response = TsiService.GetAccountTransactionsHist(inputObject); } CustomObject.member1 = response.MEMBER_1; CustomObject.member2 = response.MEMBER_2; // 报错:object does not contain a definition for MEMBER_1 and MEMBER_2, etc.
可行实现方案
方案1:使用dynamic动态类型
这是代码量最少的实现方式,直接用dynamic关键字声明接收变量,运行时会动态解析对象的属性成员,不需要在编译时明确类型:
dynamic response; if (string.IsNullOrWhiteSpace(inputObject.HistoricYear)) { response = TsiService.GetAccountTransactionsList(inputObject); } else { response = TsiService.GetAccountTransactionsHist(inputObject); } CustomObject.member1 = response.MEMBER_1; CustomObject.member2 = response.MEMBER_2; // 后续其他属性赋值逻辑和单类型写法完全一致
注意:该方案没有编译时属性名校验,如果属性名拼写错误,会在运行时抛出异常,仅适合属性数量少、逻辑简单的场景使用。
方案2:抽离公共赋值逻辑
把属性赋值的逻辑抽成独立的处理块,两个分支各自拿到强类型返回值后传入处理,既可以复用逻辑,也能保留编译时类型检查。
如果可以接受动态类型的小风险,可以用dynamic作为入参,只写一份赋值逻辑:
// 统一抽离的映射逻辑 void MapResponseToCustom(dynamic source) { CustomObject.member1 = source.MEMBER_1; CustomObject.member2 = source.MEMBER_2; // 所有属性赋值统一在这里维护 } if (string.IsNullOrWhiteSpace(inputObject.HistoricYear)) { var response = TsiService.GetAccountTransactionsList(inputObject); MapResponseToCustom(response); } else { var response = TsiService.GetAccountTransactionsHist(inputObject); MapResponseToCustom(response); }
如果需要完全的编译时检查,可以写两个重载的映射方法,虽然有两份逻辑,但结构完全一致,后续修改时同步调整即可,比散落在业务分支里更好维护:
void MapResponseToCustom(Type1 source) { CustomObject.member1 = source.MEMBER_1; CustomObject.member2 = source.MEMBER_2; } void MapResponseToCustom(Type2 source) { CustomObject.member1 = source.MEMBER_1; CustomObject.member2 = source.MEMBER_2; } // 调用逻辑和上面完全相同
方案3:模式匹配分支处理
用object接收返回值后,通过C#的模式匹配语法判断实际类型,再走对应的强类型赋值逻辑,完全不需要dynamic:
object response; if (string.IsNullOrWhiteSpace(inputObject.HistoricYear)) { response = TsiService.GetAccountTransactionsList(inputObject); } else { response = TsiService.GetAccountTransactionsHist(inputObject); } switch (response) { case Type1 t1: CustomObject.member1 = t1.MEMBER_1; CustomObject.member2 = t1.MEMBER_2; break; case Type2 t2: CustomObject.member1 = t2.MEMBER_1; CustomObject.member2 = t2.MEMBER_2; break; default: throw new InvalidOperationException("返回类型不符合预期"); }
该方案的优点是完全保留编译时类型检查,不会出现动态类型的运行时异常,缺点是需要写两份结构一致的赋值代码。
内容的提问来源于stack exchange,提问作者Sherzad
相关产品推荐
相关产品推荐

