多くの場合、複数のアルゴリズムまたは実装が同じタスクを正しく解決できます。 選択したコードが設計意図を示します。 コードを読み取る開発者とコーディング エージェントは、その構造を使用して、各操作に必要なもの、属する場所、およびいつ実行されるかという意図を推測します。
Tip
この記事は、基本的な C# の式、ステートメント、メソッドを知っている開発者向けの Fundamentals セクションの一部です。 プログラミングを初めて使用する場合は、最初に「 はじめ に」チュートリアルから始めてください。
このセクションでは、アルゴリズムに対する有効なアプローチを比較し、その動作の微妙な違いと、読者がそれらをどのように解釈するかについて説明します。 型、式、ステートメント、メソッドなどの使い慣れた言語要素を組み合わせて、意図したデザインを伝える形式を選択します。
動作と設計意図を比較する
実装を選択する前に、他のコードがそれをどのように使用するかを検討してください。 そのコードは 呼び出し元です。入力を渡し、対象の操作を呼び出し、結果を使用します。 すぐに完了した結果が必要ですか、それとも値を 1 つずつ処理できますか? この操作専用のヘルパーですか、それとも他の操作でもこのヘルパーを使用しますか? これらの質問は、必要な動作とコードが伝える設計の両方を比較するのに役立ちます。
短いインライン操作は、使用時に手順を読者に示します。 たとえば、Where(task => task.Priority > 3) を使用すると、フィルタリング ルールがすぐに見えるようになります。
Where(IsUrgent)のように、名前付きヘルパーはルールの目的を強調し、閲覧者がその詳細を個別に検査できるようにします。 ステップまたはその目的が周囲のコードを最もよく説明しているかどうかに基づいて選択します。
ヘルパーを宣言する場所は、その意図したスコープを伝えます。
ShowDashboard内のローカル関数は、ヘルパーがその操作に属し、他のメンバーがそれを呼び出せないようにすることを示します。 包含型のプライベート メソッドを使用すると、他のメンバーがヘルパーを使用できるようになり、型内の別の場所で再利用を想定していることを示唆します。 どちらも同じ計算を整理できますが、その役割について読者に異なる期待を抱かせます。
ヘルパーの入力を指定する方法は、その依存関係を伝えます。
IsUrgent(task, minimumPriority)のような制限値をパラメータとして渡すと、その入力値が呼び出しのたびに表示されるようになります。 その代わりに、ローカル関数やラムダ関数は、引数として受け取ることなく、周囲のスコープにある変数を使用してキャプチャminimumPriorityすることができます。 Capture は、ヘルパーをそれを含む操作のコンテキストに接続します。明示的なパラメーターを指定すると、渡されたデータの識別が容易になります。 周囲の状態をキャプチャしないことを保証したい場合は、static ローカル関数またはラムダを使用します。
結果を返す方法は、作業がいつ行われるかを示します。 コレクションをビルドして返すメソッドは、呼び出し元がコレクションを受け取る前にその処理を完了します。 イテレータは、呼び出し元が要求すると要素を生成するため、すべての要素が生成される前に呼び出し元が停止できます。 どちらも同じ値を生成する可能性がありますが、呼び出し元は、完了した結果を受け取っているのか、それともこれから計算されるシーケンスを受け取っているのかを理解する必要があります。
選択ごとに、最初に呼び出し元のコードに必要な動作を特定します。 次に、別の開発者またはコーディング エージェントが名前、スコープ、入力、結果から推論するものを検討します。 これらの期待がデザインに一致する形式を選択し、コードの形だけでは明らかにならない重要な動作を説明します。 目標は、どこでも1つの形式を当てはめることではなく、有効な選択肢の中から意図的に選択することです。
例の中のデザインの選択肢を読む
次の例は、複数の選択肢が設計のさまざまな部分を伝える方法を示しています。 緊急で開始する準備ができているタスクが表示されます。
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ループは、最終的なアクション、つまり各結果を書き込むことを明示的にします。
比較のために、この準備テストは単純なフィルターであるため、Where(static task => !task.IsBlocked)はReadyTasksを置き換えることができます。 どちらのフォームも、列挙されるまでフィルターを延期します。 ラムダは、呼び出しサイトにルールを配置します。ReadyTasks はルールに名前を付けます。 読者が発信者を理解するのに役立つ情報に基づいて、それらを選択します。
以下の記事では、次のような比較を使用して、各アプローチが適合するタイミングと、読者があなたの選択から推測できる内容を説明します。
次のステップ
.NET