3

我的一位开发人员朋友告诉我,使用委托的循环要快得多,我想对它进行基准测试,但我在连接它的工作原理时遇到了麻烦。

考虑以下余额计算器。这基本上需要一个账户列表,如果存在则将初始值(起始余额)添加到总贷方值(如果存在)并减去每个账户的总借方值:

    private static IDictionary<string, decimal> CalculateBalances(
        IDictionary<string, decimal> initialValue, 
        IDictionary<string, decimal> credits, 
        IDictionary<string, decimal> debits)
    {
        var r = new Dictionary<string, decimal>();

        foreach (var key in initialValue.Select(k => k.Key)
            .Concat(credits.Select(k => k.Key))
            .Concat(debits.Select(k => k.Key))
            .Distinct())
        {
            r.Add(key,
                (initialValue.ContainsKey(key) ? initialValue[key] : 0M)
                + (credits.ContainsKey(key) ? credits[key] : 0M)
                - (debits.ContainsKey(key) ? debits[key] : 0M)
                );
        }

        return r;
    }

这在中小型帐户列表上相当有效,但使用委托会更快吗?坦率地说,委托逻辑似乎与我的思维过程成直角运行,因为我正在摸索如何写这个。

任何人都可以提供一种使用委托重写它的方法吗?

4

3 回答 3

5

我假设您的朋友指的是类上的ForEach方法之List<T>类的东西。您的问题的简短回答是否定的。

等效的语法是:

initialValue.Select(k => k.Key)
            .Concat(credits.Select(k => k.Key))
            .Concat(debits.Select(k => k.Key))
            .Distinct()
            .ToList()
            .ForEach(var => r.Add(key,
                (initialValue.ContainsKey(key) ? initialValue[key] : 0M)
                + (credits.ContainsKey(key) ? credits[key] : 0M)
                - (debits.ContainsKey(key) ? debits[key] : 0M)
                ));

绝不比您上面的方式更好。它既慢又难阅读。委托调用比普通方法调用慢。您上面的语法既更快又更容易阅读。

于 2012-04-27T03:31:40.630 回答
2

任何人都可以提供一种使用委托重写它的方法吗?

但是您已经在使用代表了!这就是 lambda 被转换为的内容。当使用如此多的委托调用只是为了生成序列的每个项目时,出于性能原因是否对循环体使用委托的问题有点奇怪。

无论如何,Adam Robinson 已经介绍了如何List.ForEach对列表的每个项目执行副作用以及相关的可读性和性能影响,所以我不会深入讨论。

但是,如果 LINQ 和委托调用的边际开销不是决定因素,我将如何编写您的方法:

return initialValue
       .Concat(credits)
       .Concat(debits.Select(kvp => new KeyValuePair<string, decimal>(kvp.Key, -kvp.Value)))
       .GroupBy(kvp => kvp.Key, kvp => kvp.Value)
       .ToDictionary(group => group.Key, group => group.Sum());

现在这更具可读性。

于 2012-04-27T03:45:48.187 回答
0

如果foreach您想使用Dictionary.Add. 您始终可以从现有字典中选择来创建一个新字典,但这会比较慢。

但是,就可读性而言,这不是更容易吗?

private static decimal GetOrZero(this IDictionary<string,decimal> dict, string key)
{
    decimal value = 0;
    dict.TryGetValue(key, out value);
    return value;
}

private static IDictionary<string, decimal> CalculateBalances(
    IDictionary<string, decimal> initialValue, 
    IDictionary<string, decimal> credits, 
    IDictionary<string, decimal> debits)
{   
    var r = new Dictionary<string, decimal>();
    var accounts = initialValue.Keys.Union(debits.Keys).Union(credits.Keys);

    foreach (var accounts in accounts)
    {
        r.Add(initialValue.GetOrZero(key) + credits.GetOrZero(key) - debits.GetOrZero(key));
    }

    return r;
}
于 2012-04-27T03:51:44.137 回答