Существует ли более эффективный подход, чем делегаты действий C#, для предотвращения дублирования кода?

Насколько это возможно, я хочу избежать ненужного дублирования кода. В моем сценарии, описанном ниже, я использовал Action делегатов, чтобы избежать дублирования кода. Однако при использовании подхода Action делегата код становится медленнее примерно на 50–80 %.

Есть ли более эффективный подход с точки зрения производительности, чем делегаты Action, чтобы избежать дублирования кода в сценарии, описанном ниже?

У меня есть два метода, которые эквивалентны, за исключением самого внутреннего оператора длинного цикла:

public T[] MethodA<T>(T[] from)
{
  ...
  for (var i = 0; i < len; ++i)
  {
    var j = GetIndex(i);
    to[j] = from[i];    // This statement differs in MethodA and MethodB
  }
  ...
  return to;
}

public T[] MethodB<T>(T[] from)
{
  ...
  for (var i = 0; i < len; ++i)
  {
    var j = GetIndex(i);
    to[i] = from[j];    // This statement differs in MethodA and MethodB
  }
  ...
  return to;
}

Чтобы избежать дублирования кода, я реализовал вспомогательный метод, который принимает делегат Action. Вызов делегата заменяет оператор переменной, например:

private T[] HelperMethod<T>(T[], Action<T[], T[], int, int> action)
{
  ...
  for (var i = 0; i < len; ++i)
  {
    var j = GetIndex(i);
    action(from, to, i, j);    // Invoke the Action delegate
  }
  ...
  return to;
}

Затем я могу уменьшить MethodA и MethodB следующим образом:

public T[] MethodA<T>(T[] from)
{
  return HelperMethod(from, (src, dest, src_idx, dest_idx) => dest[dest_idx] = src[src_idx]);
}

public T[] MethodB<T>(T[] from)
{
  return HelperMethod(from, (src, dest, dest_idx, src_idx) => dest[dest_idx] = src[src_idx]);
}

Обратите внимание, что единственная разница между рефакторингом MethodA и MethodB заключается в порядке следования src_idx и dest_idx в сигнатурах Action вызовов HelperMethod.


person Anders Gustafsson    schedule 22.03.2012    source источник
comment
как вы измерили снижение производительности на 50-80%? Это звучит маловероятно   -  person BrokenGlass    schedule 22.03.2012
comment
Я провел сравнение времени в своих модульных тестах до и после рефакторинга. Конечно, приведенный выше код является упрощенной версией моей реализации, но до рефакторинга истекшее время (измеренное с помощью класса Stopwatch внутри модульного теста) составляло 1,6-1,8 секунды, тогда как после рефакторинга истекшее время составляло 2,6-2,7 секунды.   -  person Anders Gustafsson    schedule 22.03.2012
comment
Маловероятно, что прямое назначение будет лучше любой формы вызова функции, поэтому, если это критично для производительности, у вас уже есть решение, которое работает для вас...   -  person Alexei Levenkov    schedule 22.03.2012
comment
Да, вы правы, Алексей, спасибо. После дальнейшего тестирования, вдохновленного ответом democodemonkey ниже, к счастью, оказалось, что тестировать логическое значение внутри цикла было очень недорого, поэтому я решил придерживаться прямого присваивания, когда маршрут присваивания проверяется на каждой итерации цикла.   -  person Anders Gustafsson    schedule 22.03.2012
comment
Является ли цикл точно таким, как написано, или в нем есть гораздо больше кода, который вы пропустили? Вы можете переместить if сразу за пределы цикла и просто продублировать содержимое цикла... Что-то среднее.   -  person Rawling    schedule 22.03.2012
comment
Я упростил содержимое цикла в приведенном выше примере, извините за не совсем ясность. Внутри цикла тоже довольно много кода, поэтому оператор if должен быть внутри цикла, чтобы максимально избежать дублирования. В любом случае спасибо, Роулинг.   -  person Anders Gustafsson    schedule 22.03.2012


Ответы (1)


Вы можете передать методу параметр и выполнить A или B в зависимости от параметра.
Мне это не нравится, но я просто говорю, что это можно сделать. Я на самом деле предпочитаю путь действия.

person demoncodemonkey    schedule 22.03.2012
comment
Спасибо. Правда, я подумал об этом и отклонил его, потому что тогда мне нужно было бы выполнять if или switch или подобный оператор для каждого элемента в цикле. Из приведенного выше примера это не совсем очевидно, но цикл также содержит довольно много кода, который я не хочу дублировать. - person Anders Gustafsson; 22.03.2012
comment
Если подумать, я изначально отклонил этот подход, не протестировав его :-) По-видимому, оператор if чрезвычайно дешев, потому что время до и после рефакторинга примерно одинаково при использовании подхода (bool) параметра. Я пойду на это решение. Спасибо, @demoncodemonkey! - person Anders Gustafsson; 22.03.2012