理解方法和使用C#擴展方法
在C#程式中,方法是封裝可重用程式碼並執行特定任務的重要構建塊。 它們可以接受參數、返回值,並且可以被重載以處理不同的輸入。 作為更高級的概念,擴展方法允許開發者向現有型別新增功能,包括那些他們無法控制的型別。
Tim Corey 的影片 'How To Create Extension Methods in C#' 是一個很好的資源。 在本指南中,我們將探討Tim所涵蓋的幾個主題:
- 定義和調用方法
- 方法參數和引數
- 方法返回值
- 方法重載
- 實現擴展方法
定義和呼叫方法
定義實例方法
在C#中,方法是在類內定義的。 方法定義的一般語法包括存取修飾符、返回型別、方法名稱和參數。
public class SampleClass
{
public void SampleMethod()
{
// Method implementation
}
}public class SampleClass
{
public void SampleMethod()
{
// Method implementation
}
}在Tim Corey的範例中,4:05時他在一個靜態類中定義了一個方法來建立一個擴展方法。 定義的方法是 PrintToConsole。 定義包括所有一般語法,清楚地說明瞭如何用實際範例定義一個方法:
public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}呼叫一個方法
'方法呼叫'告訴程式執行在程式碼其他地方定義的特定方法,執行預定義動作。 靜態方法使用類實例調用,或者直接調用。 對於擴展方法,它們似乎是其擴展的型別的一部分。 在影片6:18,Tim展示了如何使用基本資料型別呼叫擴展方法,就像其預定義方法一樣。
string demo = "This is a demo";
demo.PrintToConsole(); // Extension method callstring demo = "This is a demo";
demo.PrintToConsole(); // Extension method call方法參數和引數
參數
參數在方法定義中指定,作為傳遞給方法的值的佔位符。 在WriteLine 方法被呼叫後,您可以在此處看到 message 是參數。
public void DisplayMessage(string message)
{
Console.WriteLine(message);
}public void DisplayMessage(string message)
{
Console.WriteLine(message);
}再次,在Tim Corey在4:05給出的擴展方法範例中,message 是參數:
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}引數
引數是方法被呼叫時傳遞的實際值。
DisplayMessage("Hello, World!"); // "Hello, World!" is the argumentDisplayMessage("Hello, World!"); // "Hello, World!" is the argument當Tim Corey在6:20使用點語法與字串型別調用該方法時,字串值實際上被作為值傳遞給 PrintToConsole 方法:
string demo = "This is a demo";
demo.PrintToConsole(); // "This is a demo" is the argumentstring demo = "This is a demo";
demo.PrintToConsole(); // "This is a demo" is the argument方法返回值
方法可以使用返回語句返回值。 返回型別在方法簽名中指定。
public int Add(int a, int b)
{
return a + b;
}public int Add(int a, int b)
{
return a + b;
}而Tim Corey影片中的擴展方法不返回值(void返回型別),您可以建立具有返回值的擴展方法。 Tim的範例中的返回型別是void,這意味著該方法不返回任何值。 以下範例顯示如何返回一個值:
public static int WordCount(this string str)
{
return str.Split(' ').Length;
}public static int WordCount(this string str)
{
return str.Split(' ').Length;
}方法重載 (11:15)
方法重載允許多個方法具有相同名稱但參數不同。 這對於建立靈活且直覺的API很有用。
public void Display(string message)
{
Console.WriteLine(message);
}
public void Display(int number)
{
Console.WriteLine(number);
}public void Display(string message)
{
Console.WriteLine(message);
}
public void Display(int number)
{
Console.WriteLine(number);
}Tim Corey在11:24簡單介紹了為不同的日誌情況建立多個方法,這可以被視為廣泛意義上的方法重載範例。 日誌方法存在兩次,一個有一個參數,另一個有兩個參數。 11:39的第二個日誌方法是日誌方法的重載版本,使其在同一名稱下具有多種功能。
Implementing Extension Methods in C
什麼是擴展方法? (3:13)
擴展方法允許您向現有型別新增新方法,而無需修改或重新編譯它們。 雖然它們被調用時似乎是實例方法,但擴展方法是定義為靜態的。
建立擴展方法
在上一章節'定義和調用方法'中,我們重點介紹了Tim Corey是如何在一個獨立的靜態類中建立擴展方法,並在其中定義靜態方法作為擴展方法使用。 以下是Tim Corey強調的一些重點:
- 確保定義一個獨立的公共靜態類,或者正如Tim所說的,"標註類為靜態,否則它將不起作用。"
- 當您建立更多擴展方法時,按型別對它們進行分組 (3:43)
- 定義一個靜態方法,其第一個參數前綴為
this關鍵字,指定要擴展的型別 (4:58)
public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}public static class Extensions
{
public static void PrintToConsole(this string message)
{
Console.WriteLine(message);
}
}呼叫擴展方法 (6:18)
接下來,Tim展示了如何在這個字串變數上呼叫擴展方法:
demo.PrintToConsole();demo.PrintToConsole();當您輸入 demo 並開始輸入 Print 時,IntelliSense會建議 PrintToConsole 方法。 這是新增至 string 型別的方法。
方法呼叫如何運作 (6:30)
Tim解释了為什麼您可以呼叫 demo.PrintToConsole():
- Demo是一個字串型別:變數
demo是string型別。 - 擴展字串型別:
string型別已擴展新方法PrintToConsole。
理解參數 (6:41)
雖然看似沒有參數傳遞給 PrintToConsole 方法,但Tim指出隱式參數傳遞 - demo 字串作為擴展方法的第一個參數傳遞。
Tim強調,擴展方法調用中比定義中少一個參數。 這是因為第一個參數(被擴展的型別)是隱式的。
擴展方法簽名
在此,this string message 表示方法擴展 string 型別,message 是隱式參數:
public static void PrintToConsole(this string message)public static void PrintToConsole(this string message)運行程式碼 (7:08)
最後,當 PrintToConsole 方法被呼叫時,會將字串輸出到控制台:
Console.WriteLine(message);Console.WriteLine(message);所以,呼叫 demo.PrintToConsole() 會在控制台中列印"This is a demo"。
使用擴展方法用於第三方類
擴展第三方類 (10:59)
Tim Corey解釋說,擴展方法可以擴展任何型別,即使是您無法直接修改的第三方類。 例如,讓我們來看 SimpleLogger 類在11:09的情況。
這裡,Tim使用假想的第三方類SimpleLogger 在11:09將消息記錄到控制台。 該類有兩個方法:
public class SimpleLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
public void Log(string message, string messageType)
{
Console.WriteLine($"{messageType}: {message}");
}
}public class SimpleLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
public void Log(string message, string messageType)
{
Console.WriteLine($"{messageType}: {message}");
}
}這些方法並不理想,因為消息型別是一個簡單的字串,可能導致不一致。 Tim建議建立擴展方法來改進此類。
實現一致的消息型別
使用擴展方法確保您的程式碼始終使用相同的消息型別和格式,從而保持一致性。 在(12:40)這裡,Tim建立了一個靜態類 ExtendSimpleLogger :
public static class ExtendSimpleLogger
{
public static void LogError(this SimpleLogger logger, string message)
{
logger.Log(message, "Error");
}
public static void LogWarning(this SimpleLogger logger, string message)
{
logger.Log(message, "Warning");
}
}public static class ExtendSimpleLogger
{
public static void LogError(this SimpleLogger logger, string message)
{
logger.Log(message, "Error");
}
public static void LogWarning(this SimpleLogger logger, string message)
{
logger.Log(message, "Warning");
}
}使呼叫更具一致性
手頭有這個資料,(14:02)他現在可以在 SimpleLogger 實例上調用擴展方法:
SimpleLogger logger = new SimpleLogger();
logger.LogError("This is an error");
logger.LogWarning("This is a warning");SimpleLogger logger = new SimpleLogger();
logger.LogError("This is an error");
logger.LogWarning("This is a warning");這確保消息型別始終是'Error'和'Warning'。
增強輸出格式 (14:35)
Tim新增了在錯誤資訊中設置控制台文字顏色的功能,以確保它們顯眼:
public static void LogError(this SimpleLogger logger, string message)
{
var defaultColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
logger.Log(message, "Error");
Console.ForegroundColor = defaultColor;
}public static void LogError(this SimpleLogger logger, string message)
{
var defaultColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
logger.Log(message, "Error");
Console.ForegroundColor = defaultColor;
}與直接方法呼叫的比較 (17:21)
Tim將這種方法與直接呼叫原始 Log 方法進行比較,這可能導致不一致:
logger.Log("Test error", "Error");
logger.Log("Another error", "ERROR");logger.Log("Test error", "Error");
logger.Log("Another error", "ERROR");這種方法容易出現打字錯誤和不一致的格式。
鍊式擴展方法 (18:13)
Tim演示了如何鍊式使用擴展方法使程式碼更具可讀性:
public static void LogInfo(this SimpleLogger logger, string message)
{
logger.Log(message, "Info");
}
public static void SaveToDatabase(this SimpleLogger logger)
{
// Simulate saving to a database
}public static void LogInfo(this SimpleLogger logger, string message)
{
logger.Log(message, "Info");
}
public static void SaveToDatabase(this SimpleLogger logger)
{
// Simulate saving to a database
}現在,您可以鍊接這些方法:
logger.LogInfo("Information").SaveToDatabase();logger.LogInfo("Information").SaveToDatabase();這使程式碼與巢狀方法呼叫相比更具可讀性和直觀性:
SaveToDatabase(LogInfo(logger, "Information"));SaveToDatabase(LogInfo(logger, "Information"));通過使用點符號和鍊接,程式碼的意圖更清晰且巢狀更少。
擴展您不擁有的東西
在20:13,Tim Corey解釋了擴展方法是為您不擁有的類(例如第三方庫)新增功能的理想選擇。 這允許在不修改原始程式碼的情況下進行增強。
避免依賴關係
Corey還強調使用擴展方法引入依賴而不直接將它們與類耦合。 例如,向 Person 類新增資料庫儲存功能而不嵌入資料庫邏輯。
擴展接口
擴展方法也可以應用於接口,如從21:30所解釋的那樣,允許實現該接口的多個類共享相同的功能。 這促進了程式碼的重用和簡化。
何時不使用擴展方法
在23:03,Tim Corey建議不要過度使用擴展方法,尤其是與基元或Microsoft提供的型別,以防止混亂和複雜性。 謹慎使用它們,僅在它們提供明顯好處時使用。
開放/封閉原則
在24:54-25:40之間,Tim推崇通過使用擴展方法新增新功能而不修改現有穩定程式碼來遵循開放/封閉原則,從而降低引入漏洞的風險。
使用語句的最佳實踐
通過邏輯分組擴展方法並將它們放在不同的名稱空間中,以避免命名衝突,便於更輕鬆的維護和除錯。
結論
現在您瞭解了定義和呼叫方法、處理參數和返回值以及利用方法重載的基礎知識。 藉由此,您可以在C#中建立強大且靈活的應用程式。
擴展方法,正如Tim Corey所解釋的,提供了一種強大的方式來增強現有型別並使您的程式碼更具可讀性和可維護性。 如需更詳細的見解和實用範例,您可以觀看Tim Corey的完整影片 How To Create Extension Methods in C#。

