LINQ to Objects,如何更新List中的项目?

时间:2011-12-14 08:44:39

标签: c# linq

我目前有以下工作代码。然而,这是非常缓慢的。我知道将所有权作为查询而不是列表传递可能会简化并加快速度,但由于各种原因,这是不可能的。

如何在不创建新列表的情况下更新到位的馆藏列表?其他改进代码的建议?

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();

    }

3 个答案:

答案 0 :(得分:6)

有一件事你可以使用联接加快速度,只评估有效的“目标”货币一次:

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 设计以更实用的方式使用。

答案 1 :(得分:2)

更新对象时不应使用LINQ。 LINQ应该是无副作用的,你应该使用它来获取你想要改变的项目,然后对它们进行预测。

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; 
        });

就像杰夫·梅尔卡多在评论中指出的那样,制作一个名单只是为了呼吁ForEach()就不是一个好主意。

答案 2 :(得分:0)

嗯,你可以在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中,有人建议将这最后一种方法重构为扩展方法,这将允许它在查询表达式中使用,并澄清意图。