LINQ到对象,如何在适当的地方更新列表中的项

本文关键字:方更新 更新 列表 LINQ 对象 | 更新日期: 2023-09-27 18:17:43

我目前有以下工作代码。然而,它的速度慢得令人痛苦。我知道以查询而不是以列表的形式传递数据可能会简化和加快速度,但由于各种原因,这是不可能的。

如何在不创建新列表的情况下更新现有的持有列表?还有其他改进代码的建议吗?

internal List<DailyHoldingItem> TransformHoldingItemsToCurrency(List<DailyHoldingItem> holdings,string toCurrency)
        {
        //TODO - how to do this calculation in place, without creating a new list?
        var query = (from holding in holdings
                     from fxRateHolding in amdw.FXRates
                     from fxRateToCur in amdw.FXRates
                     where
                     fxRateHolding.BaseCurrency == holding.Currency &&
                     fxRateToCur.BaseCurrency == toCurrency &&
                     fxRateHolding.ValueDate == holding.Date &&
                     fxRateToCur.ValueDate == holding.Date
                     select new { holding, fxRateHolding, fxRateToCur });
        return query.Select(dhi =>
        {
            decimal factor = dhi.fxRateToCur.Value / dhi.fxRateHolding.Value;
            dhi.holding.MarketValue *= factor;
            dhi.holding.Fee *= factor;
            dhi.holding.Remuneration *= factor;
            return dhi.holding;
        }).ToList();
    }

LINQ到对象,如何在适当的地方更新列表中的项

首先,你可以使用连接来加快速度,并且只评估一次有效的"目标"货币:

var targetRates = amdw.FXRates
                      .Where(rate => rate.BaseCurrency == toCurrency)
                      .ToList();
var query = from holding in holdings
            join fxRateHolding in amdw.FXRates
              on new { holding.Currency, holding.Date } equals
                 new { Currency = fxRateHolding.BaseCurrency, 
                       Date = fxRateHolding.ValueDate }
            join fxRateToCur in targetRates
              on holding.Date equals fxRateToCur.ValueDate
            select new { holding, fxRateHolding, fxRateToCur };

就我个人而言,我不会尝试就地更新列表,我不会改变现有的持有(正如您目前在Select呼叫中所做的那样)。改变现有的值往往会使你的代码更难推理——这就是为什么LINQ 被设计成以更实用的方式使用。

在更新对象时不应该使用LINQ。LINQ应该是没有副作用的,你应该用它来获得你想要改变的项目,然后对它们做一个foreach。

foreach(var dhi in query)
{
    decimal factor = dhi.fxRateToCur.Value / dhi.fxRateHolding.Value;
    dhi.holding.MarketValue *= factor;
    dhi.holding.Fee *= factor;
    dhi.holding.Remuneration *= factor;
}

您也可以像这样使用在List<T>上定义的ForEach函数。

query.ToList().ForEach(dhi =>
        {
            decimal factor = dhi.fxRateToCur.Value / dhi.fxRateHolding.Value;
            dhi.holding.MarketValue *= factor;
            dhi.holding.Fee *= factor;
            dhi.holding.Remuneration *= factor; 
        });

就像Jeff Mercado在评论中指出的那样,仅仅把ForEach()命名为列表并不是一个好主意。

你可以在SQL中做到这一点,只是要注意不是每个人都希望LINQ产生副作用,但毕竟c#不是一种函数式语言。

你可以这样做:

  • 要更新单个属性,只需执行:

    from a in ab select a.prop = newVal;

  • 要更新多个属性,您有以下选项:

    1. 将更新拆分为多个LINQ查询,每个查询更新一个属性(显然不是最好的主意)

    2. 执行from a in ab select new { x = a.prop = newVal, y = a.prop2 = newVal2 };

    3. 在LINQ方法链中(但不是在查询表达式中),您可以执行ab.Select( a=> { x = a.prop = newVal; y = a.prop2 = newVal2; return x; };

      (实际上,在Linq to objects Update中,有一个建议将最后一个方法重构为一个扩展方法,这将允许它在查询表达式中使用,并澄清意图)