重构LINQ ...从列表中乘以属性和子属性

时间:2011-08-24 01:48:06

标签: c# linq refactoring foreach

我仍然与LINQ达成协议,我正在尝试将以下foreach循环重构为它的LINQ等价物。这是我试图转换的foreach循环;

var NetTotal = 0M;

foreach (var cheque in ListOfCheques)
{
    var exchangeRate = (from exc in cheque.ExchangeRates
                        where exc.Type == EnumExchangeRate.ForCheque
                        select exc).FirstOrDefault();

    NetTotal = exchangeRate != null ? NetTotal + cheque.NetAmount * exchangeRate.Rate : NetTotal + cheque.NetAmount;
}

return NetTotal ;

和我提出的LINQ代码;

var NetTotal = (from cheque in ListOfCheques
                join exc in ListOfCheques.SelectMany(b => b.ExchangeRates) on cheque.ID equals exrate.Cheque.ID into chequeWithRate
                where income.ExchangeRates.Select(x => x.Type).Equals(EnumExchangeRate.ForCheque)
                from ur in chequeWithRate.DefaultIfEmpty()
                select ur).Sum(x => x.Cheque.NetAmount * x.Rate);

return NetTotal;

我正在努力解决的重点;

  1. Check类中的“ExchangeRates”列表可能不存在,即它不需要汇率。
  2. 如果没有找到汇率,则默认为1.如何设置...我希望在DefaultIfEmpty(1)中设置它。
  3. 非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

像任何重构一样,只是一块一块地消失。首先,而不是像foreach那样只使用Sum()

return ListOfCheques.Sum(c =>
{
    var exchangeRate = (from exc in c.ExchangeRates
        where exc.Type == EnumExchangeRate.ForCheque
        select exc).FirstOrDefault(); 
    return c.NetAmount * (exchangeRate ?? new ExchangeRate(){ Rate = 1 }).Rate;
});

(如果ExchangeRate.Rate属性的默认值为1,那就太好了)

我将exchangeRate函数重写为一种简单的格式。您确定要FirstOrDefault而不是SingleOrDefault吗?

var exchangeRate = c.ExchangeRates.
    FirstOrDefault(ex => ex.Type == EnumExchangeRate.ForCheque);

然后可以将其换成第一个语句,将结果最终产品保留在下面。

如果您想要它,可以使用一个衬垫!

return ListOfCheques.Sum(c => c.NetAmount * 
    (c.ExchangeRates.FirstOrDefault(ex => ex.Type == EnumExchangeRate.ForCheque)
        ?? new ExchangeRate() { Rate = 1 }).Rate);

修改

澄清??新的ExchangeRate()

而不是!= null ? (amount * rate) : (rate)我更喜欢使用Rate = 1的新对象来合并ExchangeRate对象。我认为这提供了更平滑,更清晰的代码。我强烈建议您将默认Rate设为1.0,然后您可以简单地与new ExchangeRate()合并,而无需设置Rate属性。

要在新的ExchangeRate对象中设置Rate的默认值,只需将初始化程序放在构造函数中

class ExchangeRate
{
    public ExchangeRate()
    {
        this.Rate = 1.0;
    }    
    // other stuff
}

答案 1 :(得分:0)

你需要这个吗?

           var query = from cheque in ListOfCheques
                        let excRates = cheque.ExchangeRates ?? Enumerable.Empty()
                        let rate = excRates.Where(x => x.Type == Something).Select(x => x.Rate).FirstOrDefault() ?? 1
                        select cheque.NetAmount * rate;

            var netTotal = query.Sum();

如果Rate是可以为null的,你可以通过使它可以为空而在let语句中熟练(例如Select(x => new int?(x.Rate))或删除?? 1并在你的select中熟练它。这将使:

           var query = from cheque in ListOfCheques
                        let excRates = cheque.ExchangeRates ?? Enumerable.Empty()
                        let rate = excRates.Where(x => x.Type == Something).Select(x => x.Rate).FirstOrDefault()
                        select cheque.NetAmount * (rate != 0 ? rate : 1);

答案 2 :(得分:0)

这个怎么样?

var query =
    from cheque in ListOfCheques
    let rate =
        cheque.ExchangeRates
            .Where(exc => exc.Type == EnumExchangeRate.ForCheque)
            .Select(exc => exc.Rate)
            .DefaultIfEmpty(1.0M)
            .First()
    select rate * cheque.NetAmount;

var NetTotal = query.Sum();

您的问题中给出的LINQ查询示例包含您未解释的“额外”内容,因此我只包含了foreach循环中的内容。