Ну, вы в основном ответили на вопрос сами.Либо передайте делегат в метод, либо унаследуйте разные реализации, и специализируйте, реализуя абстрактный метод.Другим вариантом является разделение алгоритма построения ключа как интерфейса, что приводит к лучшему разделению задач, но для простых сценариев издержки могут быть слишком большими.
Вариант 1 - делегаты
private Dictionary<string, TeamHours> BucketByProjectTask(Dictionary<string, TimeBooking> timebookings, Func<string, Task> getTaskKey)
{
…
dict[getTaskKey(task)] = th;
…
}
Подходит для использования в сильно локализованных сценариях (т. Е. Реализации и использовании private для одного класса) с помощью всего нескольких простых выражений построения ключей.
Вариант 2 - абстрактный класс и метод
class abstract BucketAlgorithm
{
protected abstract string GetTaskKey(Task task);
public Dictionary<string, TeamHours> BucketByProjectTask(Dictionary<string, TimeBooking> timebookings)
{
…
dict[GetTaskKey(task)] = th;
…
}
}
class SpecificBucketAlgorithm : BucketAlgorithm
{
protected override string GetTaskKey(Task task) { … }
}
Подходит для использования в средней области, например, в одной сборке, где нет необходимости в лучшем разделении задач (интерфейсы и реализация) или когда требуется несколько нетривиальных алгоритмов построения ключей.
Вариант 3- разложен на интерфейс
interface ITaskKeyGenerator
{
string GetTaskKey(Task task);
}
class BucketAlgorithm
{
public BucketAlgorithm(ITaskKeyGenerator taskKeyGenerator)
{
this.taskKeyGenerator = taskKeyGenerator;
}
private ITaskKeyGenerator taskKeyGenerator;
public Dictionary<string, TeamHours> BucketByProjectTask(Dictionary<string, TimeBooking> timebookings)
{
…
dict[taskKeyGenerator.GetTaskKey(task)] = th;
…
}
}
Подходит для сценариев, где требуется тщательное разделение задач или когда могут существовать несколько сложных алгоритмов построения ключей или даже они могут быть предоставлены извне пользователями API.