Собрать программное обеспечение

Часто несколько алгоритмов или реализаций могут правильно решить одну и ту же задачу. Код, который вы выбираете, передаёт замысел. Разработчики и агенты программирования, которые читают ваш код, используют его структуру, чтобы понять, что вы задумали: что требуется каждой операции, где ей место и когда она выполняется.

Tip

Эта статья является частью раздела «Основы» для разработчиков, которые знают основные выражения C#, операторы и методы. Если вы не знакомы с программированием, сначала начните с учебных пособий по началу работы.

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

Сравнить поведение и замысел дизайна

Прежде чем выбрать реализацию, рассмотрите, как другой код будет её использовать. Этот код является вызывающим кодом: он предоставляет входные данные, вызывает операцию и использует результаты. Требуется ли немедленно завершенный результат или может ли он обрабатывать значения по одному за раз? Является ли это только частью этой операции, или другие операции тоже будут использовать это? Эти вопросы помогают сравнить и необходимое вам поведение, и дизайн, который передаёт ваш код.

Короткая встроенная операция показывает читателям шаги в точке использования. Например, Where(task => task.Priority > 3) сразу делает правило фильтрации видимым. Именованный вспомогательный элемент, как и в Where(IsUrgent)случае, подчеркивает назначение правила и позволяет читателям отдельно ознакомиться с его подробностями. Выберите в зависимости от того, что лучше всего объясняет окружающий код: шаги или их назначение.

То, где вы объявляете вспомогательную функцию, говорит о её предполагаемой области видимости. Локальная функция внутри ShowDashboard указывает, что вспомогательная функция принадлежит этой операции и не позволяет другим членам вызывать её. Закрытый метод в содержащем типе делает вспомогательный метод доступным для других членов, предполагая, что вы ожидаете повторное использование в другом месте типа. Оба могут организовать один и тот же расчёт, но они приводят читателей к разным ожиданиям относительно его роли.

То, как вы предоставляете вспомогательные входные данные, показывает его зависимости. Передача ограничения в качестве параметра, например IsUrgent(task, minimumPriority), делает это входное значение видимым при каждом вызове. Локальная функция или лямбда-функция может вместо этого захватыватьminimumPriority, используя переменную из окружающей области видимости, не получая её в качестве аргумента. Захват подключает вспомогательный элемент к контексту содержащей операции; явные параметры упрощают идентификацию предоставленных данных. Используйте локальную static функцию или лямбда-выражение, если вы хотите гарантировать, что она не захватывает внешнее состояние.

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

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

Варианты оформления на примере

В следующем примере показано, как несколько вариантов отражают разные аспекты дизайна. В нем отображаются задачи, которые являются срочными и готовыми к выполнению. Объект ProjectTask хранит заголовок задачи, приоритет и заблокированное состояние.

var tasks = new[]
{
    new ProjectTask("Confirm venue", Priority: 3, IsBlocked: false),
    new ProjectTask("Print badges", Priority: 2, IsBlocked: true),
    new ProjectTask("Test microphones", Priority: 2, IsBlocked: false),
};

ShowDashboard("Community meetup", tasks, minimumPriority: 2);

static void ShowDashboard(
    string projectName,
    IEnumerable<ProjectTask> tasks,
    int minimumPriority)
{
    var urgentTasks = tasks.Where(
        task => task.Priority >= minimumPriority);

    foreach (ProjectTask task in ReadyTasks(urgentTasks))
    {
        Console.WriteLine(FormatTask(task));
    }

    string FormatTask(ProjectTask task) =>
        $"{projectName}: {task.Title}";
}

static IEnumerable<ProjectTask> ReadyTasks(
    IEnumerable<ProjectTask> tasks)
{
    foreach (ProjectTask task in tasks)
    {
        if (!task.IsBlocked)
        {
            yield return task;
        }
    }
}

record ProjectTask(string Title, int Priority, bool IsBlocked);

Каждый выбор делает понятной разные части работы:

  • Лямбда-функция, переданная в Where, помещает краткое правило приоритета рядом с операцией, которая использует ее.
  • Локальная функция FormatTask называет шаг форматирования, который относится только к ShowDashboard.
  • Итератор ReadyTasks позволяет вызывающим пользователям обрабатывать готовые задачи без создания промежуточной коллекции.
  • Цикл foreach явно показывает итоговое действие — запись каждого результата.

Для сравнения ReadyTasks может заменить Where(static task => !task.IsBlocked), так как этот тест готовности является простым фильтром. Обе формы откладывают фильтрацию до перебора. Лямбда помещает правило в место вызова; ReadyTasks дает правилу имя. Выберите между ними, исходя из того, какая информация помогает читателям понять вызывающего абонента.

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

Дальнейшие действия