通常有多種演算法或實作能正確解決同一任務。 你選擇的程式碼傳達一種設計。 開發者和程式代理會閱讀你的程式碼,利用其結構推斷你的意圖:每個操作需要什麼、該在哪裡,以及何時執行。
Tip
本文是 基礎部分, 專為熟悉基本 C# 表達式、語句與方法的開發者設計。 如果你是程式新手,建議先從 入門 教學開始。
本節比較有效方法與演算法,並解釋其行為上的細微差異及讀者如何解讀。 你會結合熟悉的語言元素,如型別、表達式、語句和方法,並選擇能傳達你想要設計的表單。
比較行為與設計意圖
在選擇實作前,請考慮其他程式碼會如何使用它。 那段程式碼就是 呼叫者:它提供輸入、呼叫你的操作,並使用結果。 它需要立即完成結果,還是可以一次處理一個數值? 輔助者是這個操作單獨的一部分,還是其他操作也會用到? 這些問題幫助你比較所需的行為與程式碼所傳達的設計。
一個短的串聯操作會顯示讀取器在使用點的步驟。 例如,能 Where(task => task.Priority > 3) 立即顯示過濾規則。 有名稱的輔助工具, Where(IsUrgent)如 ,強調規則的目的,並讓讀者能分別檢視其細節。 選擇步驟或其目的是否能最好地說明周遭程式碼。
你宣告協助者的地方,就是傳達其預期的範圍。 內部 ShowDashboard 的本地函式表示該輔助器屬於該操作,並阻止其他成員呼叫該操作。 包含型態的私有方法會讓該輔助器對其他成員開放,暗示你預期該型別會被重用。 兩者都可以組織相同的計算,但會讓讀者對其角色產生不同的期待。
你如何提供輔助者的輸入,反映了其相依關係。 將極限作為參數 IsUrgent(task, minimumPriority)傳遞,例如 ,使該輸入在每次呼叫時都能看見。 本地函式或 lambda 可以取 回minimumPriority,利用其周圍作用域的變數,但不以參數形式接收變數。 擷取將輔助工具與包含操作的上下文連結;明確的參數使所提供的資料更容易辨識。 當你想強制執行不捕捉周圍狀態時,可以使用 static 本地函式或 lambda。
你如何回報結果,反映工作何時進行。 建立並回傳集合的方法會在呼叫者收到集合前完成該工作。 迭代器會在呼叫者要求時產生元素,因此呼叫者可以在所有元素產生前停止。 兩者可能產生相同的數值,但呼叫者需要了解他們收到的是完成的結果,還是計算尚未完成的序列。
對於每個選擇,首先識別呼叫程式碼所需的行為。 接著考慮其他開發者或程式代理會從名稱、範圍、輸入和結果推論出什麼。 選擇一個能讓這些期望與你的設計相符的形式,並說明程式碼形狀無法僅從中揭示的重要行為。 目標是在有效的替代方案中做出有意識的選擇,而非在各處套用同一種形式。
請參考範例中的設計選擇
以下範例說明多個選擇如何傳達設計的不同部分。 它顯示緊迫且準備開始的任務。 A 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);
每個選擇都讓作品的不同部分保持清晰:
- 傳遞到 的
Wherelambda 會在使用該操作旁放置一個短優先權規則。 - 本地函數
FormatTask指定一個僅ShowDashboard屬於 的格式化步驟。 - 迭代器
ReadyTasks讓呼叫者能處理現成任務,而無需建立中間集合。 - 迴
foreach圈讓最終的動作——寫入每個結果——變得明確。
作為比較, Where(static task => !task.IsBlocked) 可以替換 ReadyTasks ,因為這個準備測試只是個簡單的過濾器。 兩種形式都將篩選器延後至列舉時進行。 lambda 將規則置於呼叫地點; ReadyTasks 為這條規則命名。 根據哪些資訊能幫助讀者理解來電者,在兩者之間做出選擇。
接下來的文章會用這類比較來解釋每種方法何時適合,以及讀者能從你的選擇中推斷什麼。