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

修改现有方法支持多类数据的C#最佳实践建议

问题背景

知识水平

我是C#新手,已经6年没写过面向对象代码。

上下文

我的程序从Excel读取单台电机的信息,做一些数据操作/转换,转成另一种类后写入数据库。现在想扩展现有方法,让它能处理另一类数据——这类和现有Excel类字段相似但名称不同。

核心疑问

处理多个相似但不同类的方法,最佳实践是什么?

我想到几个方案:

  • 写两个分别对应每个类的方法,但字段相似名不同是核心难点。
  • 保留一个方法,加多个判断分支——这看起来很差。
  • 把Excel类和新类合并成一个公共类,让方法只处理这个公共类。

补充说明

两类字段相似名不同的原因:

  • Excel类是从Excel读取电机信息。
  • 新类是从数据库读取的,字段和数据库完全一致(名称、数量)。
  • 我需要把这两个类的信息转换成第三个对应另一数据库的自定义类。

代码困惑

现在代码只能处理DistQuery_Excel,不知道怎么加入另一类的处理,涉及foreach循环遍历两种类型的数据:

var DistQuery_Excel =
        from motor in motor_Excel
        orderby motor.MotorName //按电机名称字母排序
        group motor by motor.MotorDist; //按配电设备分组(字符串)
var DistQuery_TEI =
        from motor in motor_TEI
        orderby motor.USER_TAG //按电机名称字母排序
        group motor by motor.Distribution_Equipment; //按配电设备分组(字符串)

foreach (var distGroup in DistQuery_Excel) //遍历每个配电设备组
{
    int cirInc = 1; //新增电路的计数器
    //查询该配电设备对应的可用电路数量
    DM_DistEquip dist = distLookupObj(distEquip_DM, distGroup.Key); //通过配电设备名称查找DM系统中的配电设备对象
    int dist_Num = dist.ixPanel; //获取配电设备的主键
    int numOfDevices = distGroup.Count(); //获取该组内的设备数量
    List<DMCircuit> circuits_Per_Dist = circuits_Avail.Where(cir => cir.ixPanel == dist_Num).OrderBy(cir => cir.iCirNum).ToList(); //获取该配电设备的可用电路,按电路号排序

    foreach (var motor in distGroup)
    {
        DMMotor motorEx = ex_Load(motor.getName()); //检查电机是否已存在于DM系统的设备列表中,存在则返回对应DMMotor对象
        //处理已有电机
        if (motorEx is not null) //如果电机已存在,更新负载信息(马力、千伏安、负载系数)
        {
            motorEx.LT_HP_1 = Convert.ToString(motor.MotorHP); //马力
            motorEx.LT_CON_1 = motor.MotorKVA; //千伏安
            //TODO: 如果负载类型变化,更新LT_FAC_1
            //TODO: 更新电压/相位
            if (circuits_Per_Dist.Any()) //如果还有可用电路
            {
                motorEx.ixCircuit1 = circuits_Per_Dist[0].ixCircuit; //分配下一个可用电路的主键
                UpdateDM_Circuit(motorEx.EQU_CALLOUT, distGroup.Key + "-" + circuits_Per_Dist[0].iCirNum, motorEx.LT_CON_1, circuits_Per_Dist[0].ixCircuit, DMpath); //更新电路信息,同步到TblCircuit表
                circuits_Per_Dist.RemoveAt(0); //从可用列表中移除已分配的电路
            }//end of if
            else //无可用电路,需要新增
            {
                motorEx.ixCircuit1 = InsertDM_Circuit(motorEx.EQU_CALLOUT, distGroup.Key + "-" + (dist.iTaps + cirInc), motorEx.LT_CON_1, dist_Num, dist.iTaps + cirInc, DMpath); //在tblCircuit表中创建新电路条目
                cirInc++;
            }//end of else
            equipList_EX.Add(motorEx); //将电机加入列表,后续会和equipList_NEW合并后同步到tblEquL表
        }//end of if
         //处理新增电机
        else //如果是新负载
        {
            DMMotor motorNew = new DMMotor().Excel2DM(motor); //将Excel电机数据转换为DM格式,赋值DMMotor属性
            if (circuits_Per_Dist.Any())
            {
                motorNew.ixCircuit1 = circuits_Per_Dist[0].ixCircuit; //分配下一个可用电路的主键
                UpdateDM_Circuit(motorNew.EQU_CALLOUT, distGroup.Key + "-" + circuits_Per_Dist[0].iCirNum, motorNew.LT_CON_1, circuits_Per_Dist[0].ixCircuit, DMpath);
                circuits_Per_Dist.RemoveAt(0); //从可用列表中移除已分配的电路
            }//end of if
            else
            {
                motorNew.ixCircuit1 = InsertDM_Circuit(motorNew.EQU_CALLOUT, distGroup.Key + "-" + (dist.iTaps + cirInc), motorNew.LT_CON_1, dist_Num, dist.iTaps + cirInc, DMpath); //在tblCircuit表中创建新电路条目
                cirInc++;
            }//end of else
            equipList_NEW.Add(motorNew); //将电机加入列表,后续会和equipList_EX合并后同步到tblEquL表
        }//end of else
    }//end of foreach
}//end of foreach
解决方案

针对你的场景,最适合的是接口+适配器模式,既不用修改原有类(比如Excel类和数据库读取的类可能是自动生成、不能随便改),又能让核心处理逻辑复用,避免重复代码或大量分支判断。

步骤1:定义公共接口

先定义一个接口,包含核心处理逻辑需要用到的所有属性/方法,把两个类的不同字段映射到统一的接口成员:

public interface IMotorData
{
    string MotorName { get; } //对应Excel类的MotorName,TEI类的USER_TAG
    string DistributionEquipment { get; } //对应Excel类的MotorDist,TEI类的Distribution_Equipment
    string MotorHP { get; } //对应Excel类的MotorHP,TEI类的对应字段(比如叫HP)
    decimal MotorKVA { get; } //对应Excel类的MotorKVA,TEI类的对应字段(比如叫KVA)
    string GetName(); //统一获取电机标识的方法
}

步骤2:为现有类实现适配器

如果不能直接修改Excel类和TEI类,就写适配器类包装它们,实现上面的接口:

Excel类适配器

public class ExcelMotorAdapter : IMotorData
{
    private readonly ExcelMotor _excelMotor; //假设你的Excel类叫ExcelMotor

    public ExcelMotorAdapter(ExcelMotor excelMotor)
    {
        _excelMotor = excelMotor;
    }

    public string MotorName => _excelMotor.MotorName;
    public string DistributionEquipment => _excelMotor.MotorDist;
    public string MotorHP => _excelMotor.MotorHP.ToString(); //类型不一致时在这里转换
    public decimal MotorKVA => _excelMotor.MotorKVA;
    public string GetName() => _excelMotor.getName();
}

TEI类适配器

public class TEIMotorAdapter : IMotorData
{
    private readonly TEIMotor _teiMotor; //假设你的TEI类叫TEIMotor

    public TEIMotorAdapter(TEIMotor teiMotor)
    {
        _teiMotor = teiMotor;
    }

    public string MotorName => _teiMotor.USER_TAG;
    public string DistributionEquipment => _teiMotor.Distribution_Equipment;
    public string MotorHP => _teiMotor.HP.ToString(); //映射到TEI类的HP字段
    public decimal MotorKVA => _teiMotor.KVA; //映射到TEI类的KVA字段
    public string GetName() => _teiMotor.GetUserTag(); //适配TEI类的方法名
}

如果可以修改原有类,直接让它们实现IMotorData接口就行,不用适配器。

步骤3:重构核心处理方法

把原来的处理逻辑改成接受IEnumerable<IMotorData>类型的参数,这样不管是Excel还是TEI的数据,都先转成IMotorData集合再传入:

//重构后的核心处理方法
public void ProcessMotorData(IEnumerable<IMotorData> motorDataList, List<DM_DistEquip> distEquip_DM, List<DMCircuit> circuits_Avail, string DMpath, List<DMMotor> equipList_EX, List<DMMotor> equipList_NEW)
{
    var distQuery =
        from motor in motorDataList
        orderby motor.MotorName
        group motor by motor.DistributionEquipment;

    foreach (var distGroup in distQuery)
    {
        int cirInc = 1;
        DM_DistEquip dist = distLookupObj(distEquip_DM, distGroup.Key);
        int dist_Num = dist.ixPanel;
        List<DMCircuit> circuits_Per_Dist = circuits_Avail.Where(cir => cir.ixPanel == dist_Num).OrderBy(cir => cir.iCirNum).ToList();

        foreach (var motor in distGroup)
        {
            DMMotor motorEx = ex_Load(motor.GetName());
            if (motorEx is not null)
            {
                motorEx.LT_HP_1 = motor.MotorHP;
                motorEx.LT_CON_1 = motor.MotorKVA;
                //TODO: 其他更新逻辑

                if (circuits_Per_Dist.Any())
                {
                    motorEx.ixCircuit1 = circuits_Per_Dist[0].ixCircuit;
                    UpdateDM_Circuit(motorEx.EQU_CALLOUT, $"{distGroup.Key}-{circuits_Per_Dist[0].iCirNum}", motorEx.LT_CON_1, circuits_Per_Dist[0].ixCircuit, DMpath);
                    circuits_Per_Dist.RemoveAt(0);
                }
                else
                {
                    motorEx.ixCircuit1 = InsertDM_Circuit(motorEx.EQU_CALLOUT, $"{distGroup.Key}-{dist.iTaps + cirInc}", motorEx.LT_CON_1, dist_Num, dist.iTaps + cirInc, DMpath);
                    cirInc++;
                }
                equipList_EX.Add(motorEx);
            }
            else
            {
                //通用转换方法,从IMotorData转成DMMotor
                DMMotor motorNew = ConvertToDMMotor(motor);
                if (circuits_Per_Dist.Any())
                {
                    motorNew.ixCircuit1 = circuits_Per_Dist[0].ixCircuit;
                    UpdateDM_Circuit(motorNew.EQU_CALLOUT, $"{distGroup.Key}-{circuits_Per_Dist[0].iCirNum}", motorNew.LT_CON_1, circuits_Per_Dist[0].ixCircuit, DMpath);
                    circuits_Per_Dist.RemoveAt(0);
                }
                else
                {
                    motorNew.ixCircuit1 = InsertDM_Circuit(motorNew.EQU_CALLOUT, $"{distGroup.Key}-{dist.iTaps + cirInc}", motorNew.LT_CON_1, dist_Num, dist.iTaps + cirInc, DMpath);
                    cirInc++;
                }
                equipList_NEW.Add(motorNew);
            }
        }
    }
}

//新增的通用转换方法
private DMMotor ConvertToDMMotor(IMotorData motorData)
{
    return new DMMotor
    {
        LT_HP_1 = motorData.MotorHP,
        LT_CON_1 = motorData.MotorKVA,
        //其他属性按需求映射
    };
}

步骤4:调用重构后的方法

分别把Excel和TEI的数据转成IMotorData集合,传入同一个方法:

//处理Excel数据
var excelMotorData = motor_Excel.Select(m => new ExcelMotorAdapter(m));
ProcessMotorData(excelMotorData, distEquip_DM, circuits_Avail, DMpath, equipList_EX, equipList_NEW);

//处理TEI数据
var teiMotorData = motor_TEI.Select(m => new TEIMotorAdapter(m));
ProcessMotorData(teiMotorData, distEquip_DM, circuits_Avail, DMpath, equipList_EX, equipList_NEW);

方案优势

  • 复用核心逻辑:不用写两个几乎一样的方法,避免代码重复。
  • 可扩展性:以后再加新的电机数据类,只需要新增一个适配器实现IMotorData就行,不用改核心处理代码。
  • 低耦合:依赖抽象(接口)而不是具体实现,符合面向对象设计原则。

简单场景替代方案

如果场景很简单,不想写接口和适配器,也可以用匿名类型先统一字段名,再传入处理:

//统一Excel数据的字段名
var unifiedExcel = motor_Excel.Select(m => new {
    MotorName = m.MotorName,
    DistributionEquipment = m.MotorDist,
    MotorHP = m.MotorHP.ToString(),
    MotorKVA = m.MotorKVA,
    GetName = () => m.getName()
});

//统一TEI数据的字段名
var unifiedTEI = motor_TEI.Select(m => new {
    MotorName = m.USER_TAG,
    DistributionEquipment = m.Distribution_Equipment,
    MotorHP = m.HP.ToString(),
    MotorKVA = m.KVA,
    GetName = () => m.GetUserTag()
});

但匿名类型只能在局部使用,无法跨方法传递,适合简单的一次性处理。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 06:16:18