简体   繁体   English

在这种情况下我如何使用委托?

[英]How would I use a delegate in this scenario?

A developer friend of mine tells me that loops are much faster using delegates, and I'd like to benchmark it, but I'm having trouble connecting the dots on how it works. 我的一个开发人员朋友告诉我,使用委托的循环要快得多,而且我想对它进行基准测试,但是我无法将它们的工作原理连接起来。

Consider the following balance calculator. 考虑以下平衡计算器。 This basically takes a list of accounts and adds the initial value (starting balance) if it exists to the total credits value if it exist and subtracts the total debits value for each account: 这基本上是一个帐户列表,如果它存在,则将初始值(起始余额)添加到总信用值(如果存在)并减去每个帐户的总借方值:

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

This is fairly performant on small to medium account lists, but would using delegates be faster? 这在中小型帐户列表中是相当高效的,但使用委托会更快吗? And frankly, delegate logic seems to be operating at right angles to my thought processes, because I'm scratching my head how to even write this. 坦率地说,委托逻辑似乎与我的思维过程正确地运作,因为我正在摸不着如何写这个。

Can anyone offer a way to rewrite this using delegates? 任何人都可以提供一种使用委托重写这个的方法吗?

I'm assuming your friend is referring to something like the ForEach method on the List<T> class. 我假设你的朋友指的是List<T>类上的ForEach方法。 The short answer to your question is no . 对你的问题的简短回答是否定的

The equivalent syntax would be: 等效的语法是:

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

This is in no way better than the way you have it above. 绝不比你上面的方式更好。 It is both slower and more difficult to read. 它既慢又难以阅读。 Delegate invocation is slower than ordinary method invocation. 委托调用比普通方法调用慢。 The syntax you have above is both faster and easier to read. 您上面的语法更快,更容易阅读。

Can anyone offer a way to rewrite this using delegates? 任何人都可以提供一种使用委托重写这个的方法吗?

But you are using delegates already! 但是你已经使用代表了! That's what the lambdas are being converted to. 这就是lambda被转换成的东西。 The question about whether to use a delegate or not for the loop-body for performance reasons is a little strange when so many delegate invocations are being used just to produce each item of the sequence. 出于性能原因,是否使用委托是否为循环体的问题有点奇怪,因为如此多的委托调用仅用于生成序列的每个项目。

Anyway, Adam Robinson has already covered how you would List.ForEach to execute a side-effect on each item of a list and the associated readability and performance implications, so I won't go into that. 无论如何,Adam Robinson已经介绍了List.ForEach如何对列表中的每个项目执行副作用以及相关的可读性和性能影响,所以我不会涉及到这一点。

But here's how I would write your method if the marginal overhead of LINQ and delegate invocations were not a deciding factor: 但是,如果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());

Now that's much more readable. 现在这更具可读性了。

The foreach construct is the right one if you want to build a new dictionary using Dictionary.Add . 如果要使用Dictionary.Add构建新字典,则foreach构造是正确的构造。 You can always just select from the existing dictionary to create a new one, but that will be slower. 您始终可以从现有字典中选择以创建新字典,但这样会更慢。

However, as to readability, isn't this much easier? 但是,至于可读性,这不是更容易吗?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在这种情况下,我将如何使用LINQ2XML? - How would I use LINQ2XML in this scenario? 我如何使用委托作为参数调用此函数 - how would i call this function with a delegate as a parameter 在C#中,我将如何使用委托来操纵我创建的自定义类中的表单? - In C#, how would I use a delegate to manipulate a form from a custom class I made? 如何将匿名结果类型用于 LINQ 到 SQL 的表达式委托? - How would I use an anonymous result type for an expression delegate for LINQ to SQL? 我什么时候在 asp.net 中使用委托? - When would I use a delegate in asp.net? C#:在这种情况下,如何过滤掉不需要的命名空间? - C#: How would I filter out the unneeded namespaces in this scenario? 如何正确构造此SpecFlow功能/场景集? - How would I structure this SpecFlow Feature/Scenario set correctly? 在这种情况下如何使用ExecuteScalar? - How can I use ExecuteScalar in this scenario? 在这种情况下,如何正确使用.OrderBy() - How do I correctly use .OrderBy() in this scenario C# 如何通过反射获得一个字段并创建一个返回其值以供将来使用的委托? - C# How would I get a field through reflection once and make a delegate that returns its value for future use?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM