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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 02:33:27