理解方法和使用 C# 扩展方法
在 C# 编程中,方法是重要的构建模块,用于封装可重用代码并执行特定任务。 它们可以接受参数、返回值,并且可以被重载以处理不同的输入。 扩展方法是一种更高级的概念,它允许开发人员向现有类型(包括他们无法控制的类型)添加功能。
Tim Corey的视频"如何在 C# 中创建扩展方法"是一个非常好的资源。 在本指南中,我们将探讨蒂姆涉及的几个主题:
- 定义和调用方法
- 方法参数和参数
- 方法返回值
- 方法重载
- 实现扩展方法
定义和调用方法
已定义实例方法
在 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方法参数和实参
参数
参数在方法定义中指定,并作为传递给方法的值的占位符。 在调用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方法返回值
方法可以使用 return 语句返回值。 返回类型在方法签名中指定。
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 简要地谈到了为不同的日志记录场景创建多个方法,这可以看作是更广泛意义上的方法重载的一个例子。 log 方法有两个版本,一个版本有一个参数,另一个版本有两个参数。 11:39 处的第二个日志方法是日志方法的重载版本,它以相同的名称提供了多种功能。
Implementing Extension Methods in C
什么是扩展方法? (3:13)
扩展方法允许你向现有类型添加新方法,而无需修改或重新编译它们。 虽然它们被称作实例方法,但扩展方法被定义为静态方法。
创建扩展方法
在上一节"定义和调用方法"中,我们重点介绍了 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();当输入PrintToConsole方法。 这是添加到string类型的新方法。
方法调用的工作原理(6:30)
Tim解释了为什么您可以调用demo.PrintToConsole():
- Demo是一个字符串类型:变量
string类型。 - 扩展的字符串类型:
PrintToConsole进行了扩展。
理解参数(6:41)
虽然似乎没有参数传递给demo字符串作为第一个参数传递给扩展方法。
Tim 强调,扩展方法在调用时比其定义中少一个参数。 这是因为第一个参数(被扩展的类型)是隐式的。
扩展方法签名
在这里,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解释说,扩展方法可以扩展任何类型,甚至是您无法直接修改的第三方类。 例如,让我们看看11:09的SimpleLogger类。
在这里,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");这样可以确保消息类型始终为"错误"和"警告"。
增强输出格式(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 关于如何在 C# 中创建扩展方法的完整视频。

