如何在C#中实现符合指定规则的MergeItem合并方法?
搞定Items类的MergeItem方法实现!
嘿,我来帮你解决这个合并逻辑的问题~你的思路方向是对的,Linq确实能帮我们高效实现需求,但需要结合业务规则来处理「更新现有项、添加新项」的逻辑,而不是单纯用Union或者Concat。我们直接来看符合要求的实现:
完整的MergeItem方法实现
public void MergeItem(Items sourceItem) { if (sourceItem?.ItemsLines == null) return; // 空参数直接返回,避免踩空引用的坑 foreach (var sourceLine in sourceItem.ItemsLines) { // 找当前集合里有没有相同ItemLineId的项 var existingLine = ItemsLines.FirstOrDefault(line => line.ItemLineId == sourceLine.ItemLineId); if (existingLine != null) { // 按规则3合并:累加数量,算加权平均成本(比简单平均更合理哦) var totalQuantity = existingLine.Quantity + sourceLine.Quantity; var totalCostAmount = existingLine.Cost * existingLine.Quantity + sourceLine.Cost * sourceLine.Quantity; existingLine.Quantity = totalQuantity; existingLine.Cost = totalCostAmount / totalQuantity; } else { // 规则4:没找到就加新项,建议创建新实例避免外部修改影响原集合 ItemsLines.Add(new ItemsLine(sourceLine.ItemLineId, sourceLine.Quantity, sourceLine.Cost)); } } }
为啥这个实现符合你的规则?
我们逐条对应你给的合并规则:
- 规则1:遍历传入的sourceItem的每一行ItemsLine,逐个处理更新或添加
- 规则2:通过
FirstOrDefault基于ItemLineId查找,保证了ID唯一性的处理逻辑 - 规则3:这里用了加权平均成本(不是简单把两个成本相加除以2),比如原有4个10.33的,新增1个5.22的,总成本是410.33+15.22,平均成本按总金额除以总数量算,更贴合实际业务
- 规则4:找不到匹配ID的项时,直接添加新的ItemsLine
说说你之前尝试的代码问题
你之前写的这段:
IEnumerable<ItemsLine> items = new List<ItemsLine>(); IEnumerable<Items> items = ItemsLines.Union<item>()
有几个明显的问题:
- 重复定义了变量
items,而且类型还不一致(一会是ItemsLine集合,一会是Items集合) Union默认是按对象引用相等来合并的,不会自动识别ItemLineId的匹配,更没法实现「合并数量、平均成本」的业务逻辑- 泛型参数
Union<item>写错了,应该是Union<ItemsLine>,但就算改对了,也只是简单合并两个集合,完全不符合你的规则要求
可选的Linq简洁写法(适合不需要保留原实例的场景)
如果你偏爱用Linq写更简洁的代码,也可以用GroupBy来合并两个集合,然后重新构建ItemsLines:
public void MergeItem(Items sourceItem) { if (sourceItem?.ItemsLines == null) return; // 合并两个集合,按ItemLineId分组计算 var mergedLines = ItemsLines.Concat(sourceItem.ItemsLines) .GroupBy(line => line.ItemLineId) .Select(group => { var totalQty = group.Sum(line => line.Quantity); var totalCost = group.Sum(line => line.Cost * line.Quantity); return new ItemsLine(group.Key, totalQty, totalCost / totalQty); }); // 替换原集合(如果允许替换的话,也可以先清空原集合再AddRange) ItemsLines = mergedLines.ToList(); }
这种写法代码更短,但要注意:它会重新创建所有ItemsLine实例,如果原集合里的实例有其他关联逻辑(比如绑定到UI),可能需要谨慎使用哦。
内容的提问来源于stack exchange,提问作者h d




