2012-04-27 2 views
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; 
    } 

Это довольно производительной в списках малых и средних учетных записей, но будет ли использование делегатов быстрее? И, честно говоря, делегатская логика, похоже, работает под прямым углом к ​​моим мысленным процессам, потому что я почесываю голову, как даже написать это.

Может ли кто-нибудь предложить способ переписать это с помощью делегатов?

+0

Делегаты должны быть быстрее, чем, точно? Вы уже используете делегатов для своих лямбдов (даже если вы их явно не создали, они там). Какая часть вашей петли, если бы ваш друг заменил вас делегатами, точно? – Sven

+0

То, как она говорила об этом, в основном заменяя все вещи на внутренней части цикла некоторым вызовом делегата. Мне удалось поднять скептическую бровь, как будто я знал, о чем она говорит. –

+0

«Делегатная логика, кажется, работает под прямым углом к ​​моим мысленным процессам», создайте игрушечный проект на функциональном языке программирования (скажем, F #), и что «вектор мысли» обязательно повернется в правильном направлении! – dasblinkenlight

ответ

5

Я предполагаю, что ваш друг ссылается на что-то вроде метода ForEach на класс List<T>. Короткий ответ на ваш вопрос: no.

Эквивалентный синтаксис будет:

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

Это ни в коей мере лучше, чем то, как вы имеете его выше . Это медленнее и труднее читать. Вызов делегата происходит медленнее, чем обычный вызов метода. Синтаксис, который у вас выше, быстрее и легче читать.

+0

Хм. Тогда в каких обстоятельствах метод 'ForEach' был бы привлекательным для объекта« List »? –

+0

@JeremyHolovacs: Я не могу придумать обстоятельства, в которых было бы лучше *. –

+0

Когда я получу списки, я постараюсь запомнить эти тесты. Это должно быть интересно посмотреть. –

2

Может ли кто-нибудь предложить способ переписать это с помощью делегатов?

Но вы :, используя делегатов уже! Это то, к чему превращаются лямбды. Вопрос о том, использовать ли делегат или нет для тела цикла по соображениям производительности, немного странно, когда так много вызовов делегатов используются только для создания каждого элемента последовательности.

В любом случае, Адам Робинсон уже рассмотрел, как вы должны были бы сделать побочный эффект для каждого элемента списка и связанных с ним читабельности и производительности, поэтому я не буду вникать в это.

Но вот как бы я написать свой метод, если предельная накладные расходы 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()); 

Теперь, гораздо более удобным для чтения.

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; 
} 
Смежные вопросы