跳至頁尾內容
Iron Academy Logo
C#常見問題

在 C# 中重構條件 if:避免條件雜亂與 Derek Comartin

[[academy-video-youtube({"vid": "QoK7jSZ-viw", "start_time": "0", "title": "枚舉並不可怕。 到處都是條件式", "creator": "Derek Comartin", "length": "11m 03s"})]]

在 C# 中,條件語句如 if 語句、if else 語句和 switch 語句是基本工具。 但是,當這些結構被過度使用時會發生什麼——特別是與枚舉綁定時? Derek Comartin 在他的影片"枚舉並不可怕。到處都是條件式 在這個影片中,Derek帶我們透過一個詳細的重構,用更清晰、更易於維護的模式替換掉廣泛的條件邏輯。

在這篇文章中,我們將一步步走過Derek的思路,使用他的時間戳作為錨點。 我們還會探索他的想法如何應用於 C# 中常見的條件模式,如三元運算符、else 語句和 switch-case 結構——突出它們在大型程式碼庫中的問題以及如何重構以達到更好的設計。

條件式爆炸:真正的問題

Derek 首先展示了一個檢查產品型別的 if 語句:

if (productType == ProductType.Template || productType == ProductType.Ebook)
if (productType == ProductType.Template || productType == ProductType.Ebook)

乍看之下,上面的條件 if 看起來很直接。 但是Derek警告,這種語句評估給定條件,然後僅在條件為真時執行程式碼塊——這當這種邏輯到處重複時變成了一個問題。

您可能會在另一個方法或類中再次看到這個塊:

if (offeringType == ProductType.Template || offeringType == ProductType.Ebook)
if (offeringType == ProductType.Template || offeringType == ProductType.Ebook)

Derek 解釋說,這種模式很快就在大型系統中蔓延。 相同的 if else 邏輯出現在多個服務中,當新增新的枚舉值時會造成不一致和錯誤。 例如,當您引入新的產品型別如 Video 時會怎樣? 您將需要記得更新每一個包含此條件表達式的程式碼塊。

重複放大了複雜性

在以下範例中,Derek深入巢狀條件。 在一種方法中,一個 if else 語句檢查相同的枚舉,並將結果傳遞給另一種方法,該方法也包含類似的檢查。

語句檢查 Template 或 Ebook,並返回某些東西——否則返回 null。 Derek 指出,這種冗餘不僅使程式碼更長,還帶來維護風險。 相同的邏輯被複製到多個文件中,導致控制流程混亂。

如果您的系統要求您每次觸摸枚舉時都要新增預設情況,那麼您就知道有些地方出錯了。

改變我們對條件式的思考方式

而不是不斷使用 if else 檢查型別,Derek 建議問一個更好的問題:

產品是否具有可下載的特性?

這對意圖的表達更好。 它使您的程式碼更具可讀性並減少對枚舉的依賴。 而不是編寫一個具有兩個條件的 if 語句如:

if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)
if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)

您可以簡單地在模型中封裝該邏輯並編寫:

if (product.HasDownloadableResource())
if (product.HasDownloadableResource())

僅在存在可下載資源時返回 true——減少對複雜條件表達式的需求。

從 If 語句到封裝行為

為了解決核心問題,Derek 引入了 DownloadableResource 型別。 此型別包括一個下載 URL 和一個預設文件名。 它成為您的領域中的一級部分,而非依賴於 if 語句來推導出它。

現在,取而代之重複這個:

if (product.Type == ProductType.Template)
{
    // Generate file name
}
else if (product.Type == ProductType.Ebook)
{
    // Generate file name
}
if (product.Type == ProductType.Template)
{
    // Generate file name
}
else if (product.Type == ProductType.Ebook)
{
    // Generate file name
}

您編寫這個:

var downloadable = product.GetDownloadableResource();
if (downloadable != null)
{
    Console.WriteLine(downloadable.FileName);
}
var downloadable = product.GetDownloadableResource();
if (downloadable != null)
{
    Console.WriteLine(downloadable.FileName);
}

這大大簡化了邏輯並消除了對 else 語句分支甚至 switch 語句的需要。

運行時超越編譯時:一個戰略轉變

Derek 更進一步,解釋了一個重要的設計選擇:將邏輯從編譯時轉換為運行時。這意味著在運行時查詢系統以查看產品是否存在 DownloadableResource。 如果存在,則採取行動。 如果不,在跳過它。

他指出,此舉將靜態 if else 邏輯轉變為運行時查詢。 這可能會增加一個資料庫調用,但它減少了巢狀的 if else 邏輯並集中行為。 這提高了大規模的可維護性。

使用繼承來管理可下載產品

Derek 探索的另一種方法是繼承。 您可以建立一個抽象基類 Product,然後定義派生型別如 Ebook、Template 或 OfflineCourse。

每一種都覆蓋方法如:

public virtual string GetDownloadUrl() { ... }
public virtual string GetDownloadUrl() { ... }

這種方法允許每個產品自行處理邏輯。 雖然這避免了 switch 語句或多個條件語句,但 Derek 指出,如果不小心,您仍可能會在內部編寫條件表達式。

不使用繼承仍能更好地封裝

如果繼承顯得過於繁瑣,Derek 建議使用明確的型別,如 DownloadableProduct,它包含自己的屬性和方法——而不是被綁定到一個層次結構上。

在您的程式中,它可能看起來像這樣:

var downloader = new DownloadableProduct(product);
Console.WriteLine(downloader.GetDefaultFileName());
var downloader = new DownloadableProduct(product);
Console.WriteLine(downloader.GetDefaultFileName());

不需要使用 if else 或 switch 語句來決定行為——每個物件知道要做什麼。

輕量解決方案:在枚舉上使用擴展方法

如果您還沒準備好放棄枚舉,Derek 提出了一個輕量解決方案——建立擴展方法:

public static bool IsDownloadable(this ProductType type)
{
    return type == ProductType.Template || type == ProductType.Ebook;
}
public static bool IsDownloadable(this ProductType type)
{
    return type == ProductType.Template || type == ProductType.Ebook;
}

現在不用編寫:

if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)
if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)

您可以將其簡化為:

if (product.Type.IsDownloadable())
if (product.Type.IsDownloadable())

這將您的邏輯集中化,避免一次又一次地重複大括號和程式碼塊。

避免過度使用三元運算符和 switch

Derek 也警告不要過度使用如三元運算符等簡寫:

string filename = product.Type == ProductType.Template ? "template.pdf" : "default.pdf";
string filename = product.Type == ProductType.Template ? "template.pdf" : "default.pdf";

雖然這是有效的語法,但在邏輯變得複雜時,它可能會容易出錯且難以閱讀。 尤其是當條件判斷為假時,可能會以微妙的方式分配錯誤的值。

同樣地,帶有 break 語句和預設情況的 switch 也陷入這個陷阱。更好的是請物件給予行為而不是使用 switch-case 邏輯。

結論:更智能的控制與更少的條件群雜

總之,Derek 的影片不僅僅是批評枚舉——而是對我們如何在它們周圍使用條件 if 結構的批判。 通過將 if else 和 switch 語句分散到程式碼庫中,您使得系統更加難以測試、維護和演化。

無論您選擇封裝、運行時查詢、繼承還是簡單的擴展方法,目標仍然是:減少條件式,並將邏輯移動到其應所在的位置。

記住:

  • 條件式並不壞。

  • 條件群雜是問題。

  • 優雅的程式碼不依賴於類中零散的多個 if else 語句。

  • 評估您的上下文並據此重構。

正如 Derek 所說,"這取決於您的上下文。" 但有一點是確定的:產品不僅僅只是產品——有時它是重新思考您設計的一個信號。

Hero Worlddot related to 在 C# 中重構條件 if:避免條件雜亂與 Derek Comartin
Hero Affiliate related to 在 C# 中重構條件 if:避免條件雜亂與 Derek Comartin

分享您所愛以賺取更多報酬

您是否為使用 .NET、C#、Java、Python 或 Node.js 的開發者建立內容?將您的專業知識轉化為額外收入!

Iron 支援團隊

我們線上24小時,每週5天。
聊天
電子郵件
給我打電話