Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
28m 27s
在 C# 编程中,方法是重要的构建模块,用于封装可重用代码并执行特定任务。 它们可以接受参数、返回值,并且可以被重载以处理不同的输入。 扩展方法是一种更高级的概念,它允许开发人员向现有类型(包括他们无法控制的类型)添加功能。
Tim Corey的视频"如何在 C# 中创建扩展方法"是一个非常好的资源。 在本指南中,我们将探讨蒂姆涉及的几个主题:
在 C# 中,方法是在类中定义的。 方法定义的通用语法包括访问修饰符、返回类型、方法名称和参数。
public class SampleClass
{
public void SampleMethod()
{
// Method implementation
}
}Public Class SampleClass
Public Sub SampleMethod()
' Method implementation
End Sub
End ClassPublic Class SampleClass
Public Sub SampleMethod()
' Method implementation
End Sub
End Class在 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);
}
}Public Module Extensions
<System.Runtime.CompilerServices.Extension> _
Public Sub PrintToConsole(ByVal message As String)
Console.WriteLine(message)
End Sub
End ModulePublic Module Extensions
<System.Runtime.CompilerServices.Extension> _
Public Sub PrintToConsole(ByVal message As String)
Console.WriteLine(message)
End Sub
End Module"方法调用"告诉程序执行代码中其他地方定义的特定方法,从而执行预定义的操作。 方法可以通过类实例调用,如果是静态方法,则可以直接调用。 对于扩展方法,它们看起来就像是它们所扩展的类型的一部分。 在视频 6 分 18 秒处,Tim 展示了如何像调用预定义方法一样,使用原始数据类型调用扩展方法。
string demo = "This is a demo";
demo.PrintToConsole(); // Extension method callstring demo = "This is a demo";
demo.PrintToConsole(); // Extension method callDim demo As String = "This is a demo"
demo.PrintToConsole() ' Extension method callDim demo As String = "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);
}Public Sub DisplayMessage(message As String)
Console.WriteLine(message)
End SubPublic Sub DisplayMessage(message As String)
Console.WriteLine(message)
End Sub再次,在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);
}<Extension()>
Public Sub PrintToConsole(ByVal message As String)
Console.WriteLine(message)
End Sub<Extension()>
Public Sub PrintToConsole(ByVal message As String)
Console.WriteLine(message)
End Sub参数是调用方法时传递给该方法的实际值。
DisplayMessage("Hello, World!"); // "Hello, World!" is the argumentDisplayMessage("Hello, World!"); // "Hello, World!" is the argumentDisplayMessage("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 argumentDim demo As String = "This is a demo"
demo.PrintToConsole() ' "This is a demo" is the argumentDim demo As String = "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;
}Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End FunctionPublic Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function虽然 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;
}<Extension()>
Public Function WordCount(ByVal str As String) As Integer
Return str.Split(" "c).Length
End Function<Extension()>
Public Function WordCount(ByVal str As String) As Integer
Return str.Split(" "c).Length
End Function方法重载允许多个方法具有相同的名称但参数不同。 这有助于创建灵活直观的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);
}Public Sub Display(message As String)
Console.WriteLine(message)
End Sub
Public Sub Display(number As Integer)
Console.WriteLine(number)
End SubPublic Sub Display(message As String)
Console.WriteLine(message)
End Sub
Public Sub Display(number As Integer)
Console.WriteLine(number)
End SubTim Corey 在 11:24 简要地谈到了为不同的日志记录场景创建多个方法,这可以看作是更广泛意义上的方法重载的一个例子。 log 方法有两个版本,一个版本有一个参数,另一个版本有两个参数。 11:39 处的第二个日志方法是日志方法的重载版本,它以相同的名称提供了多种功能。
扩展方法允许你向现有类型添加新方法,而无需修改或重新编译它们。 虽然它们被称作实例方法,但扩展方法被定义为静态方法。
在上一节"定义和调用方法"中,我们重点介绍了 Tim Corey 如何在单独的静态类中创建扩展方法,并在其中定义静态方法以用作扩展方法。 以下是蒂姆·科里强调的一些关键点:
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);
}
}Public Module Extensions
<System.Runtime.CompilerServices.Extension> _
Public Sub PrintToConsole(ByVal message As String)
Console.WriteLine(message)
End Sub
End ModulePublic Module Extensions
<System.Runtime.CompilerServices.Extension> _
Public Sub PrintToConsole(ByVal message As String)
Console.WriteLine(message)
End Sub
End Module接下来,Tim 将演示如何对该字符串变量调用扩展方法:
demo.PrintToConsole();demo.PrintToConsole();demo.PrintToConsole()demo.PrintToConsole()当您输入PrintToConsole方法。 这是新添加到string类型的方法。
Tim解释了为什么您可以调用demo.PrintToConsole():
string类型。PrintToConsole进行扩展。虽然似乎没有参数传递给PrintToConsole方法,Tim指出了隐式参数传递 - demo字符串作为第一个参数传递给扩展方法。
Tim 强调,扩展方法在调用时比其定义中少一个参数。 这是因为第一个参数(被扩展的类型)是隐式的。
这里,message是隐式参数:
public static void PrintToConsole(this string message)public static void PrintToConsole(this string message)<Extension()>
Public Sub PrintToConsole(ByVal message As String)<Extension()>
Public Sub PrintToConsole(ByVal message As String)最后,当PrintToConsole方法被调用时,它将字符串输出到控制台:
Console.WriteLine(message);Console.WriteLine(message);Console.WriteLine(message)Console.WriteLine(message)因此,调用demo.PrintToConsole()会在控制台打印"这是一个演示"。
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}");
}
}Public Class SimpleLogger
Public Sub Log(message As String)
Console.WriteLine(message)
End Sub
Public Sub Log(message As String, messageType As String)
Console.WriteLine($"{messageType}: {message}")
End Sub
End ClassPublic Class SimpleLogger
Public Sub Log(message As String)
Console.WriteLine(message)
End Sub
Public Sub Log(message As String, messageType As String)
Console.WriteLine($"{messageType}: {message}")
End Sub
End Class这些方法并不理想,因为消息类型是简单的字符串,这可能会导致不一致。 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");
}
}Public Module ExtendSimpleLogger
<System.Runtime.CompilerServices.Extension>
Public Sub LogError(ByVal logger As SimpleLogger, ByVal message As String)
logger.Log(message, "Error")
End Sub
<System.Runtime.CompilerServices.Extension>
Public Sub LogWarning(ByVal logger As SimpleLogger, ByVal message As String)
logger.Log(message, "Warning")
End Sub
End ModulePublic Module ExtendSimpleLogger
<System.Runtime.CompilerServices.Extension>
Public Sub LogError(ByVal logger As SimpleLogger, ByVal message As String)
logger.Log(message, "Error")
End Sub
<System.Runtime.CompilerServices.Extension>
Public Sub LogWarning(ByVal logger As SimpleLogger, ByVal message As String)
logger.Log(message, "Warning")
End Sub
End Module通过此操作(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");Dim logger As New SimpleLogger()
logger.LogError("This is an error")
logger.LogWarning("This is a warning")Dim logger As New SimpleLogger()
logger.LogError("This is an error")
logger.LogWarning("This is a warning")这样可以确保消息类型始终为"错误"和"警告"。
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;
}<Extension()>
Public Sub LogError(ByVal logger As SimpleLogger, ByVal message As String)
Dim defaultColor = Console.ForegroundColor
Console.ForegroundColor = ConsoleColor.Red
logger.Log(message, "Error")
Console.ForegroundColor = defaultColor
End Sub<Extension()>
Public Sub LogError(ByVal logger As SimpleLogger, ByVal message As String)
Dim defaultColor = Console.ForegroundColor
Console.ForegroundColor = ConsoleColor.Red
logger.Log(message, "Error")
Console.ForegroundColor = defaultColor
End SubTim将这种方法与直接调用原始Log方法进行了比较,这可能导致不一致:
logger.Log("Test error", "Error");
logger.Log("Another error", "ERROR");logger.Log("Test error", "Error");
logger.Log("Another error", "ERROR");logger.Log("Test error", "Error")
logger.Log("Another error", "ERROR")logger.Log("Test error", "Error")
logger.Log("Another error", "ERROR")这种方法容易出现拼写错误和格式不一致的问题。
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
}Public Sub LogInfo(ByVal logger As SimpleLogger, ByVal message As String)
logger.Log(message, "Info")
End Sub
Public Sub SaveToDatabase(ByVal logger As SimpleLogger)
' Simulate saving to a database
End SubPublic Sub LogInfo(ByVal logger As SimpleLogger, ByVal message As String)
logger.Log(message, "Info")
End Sub
Public Sub SaveToDatabase(ByVal logger As SimpleLogger)
' Simulate saving to a database
End Sub现在,您可以将这些方法串联起来使用:
logger.LogInfo("Information").SaveToDatabase();logger.LogInfo("Information").SaveToDatabase();logger.LogInfo("Information").SaveToDatabase()logger.LogInfo("Information").SaveToDatabase()与嵌套方法调用相比,这使得代码更易读、更直观:
SaveToDatabase(LogInfo(logger, "Information"));SaveToDatabase(LogInfo(logger, "Information"));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# 中创建扩展方法的完整视频。
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy