扩展方法 - 概述
扩展方法在 C#3.0 中引入。扩展方法扩展并向现有类型添加行为,而不创建新的派生类型,重新编译或以其他方式修改原始类型。*当你无法修改要增强的类型的源时,它们尤其有用。*可以为系统类型,由第三方定义的类型以及你自己定义的类型创建扩展方法。可以调用扩展方法,就好像它是原始类型的成员方法一样。这允许方法链用于实现 Fluent 接口。
通过向静态类添加静态方法来创建扩展方法,该静态类与要扩展的原始类型不同。持有扩展方法的静态类通常是为了保存扩展方法而创建的。
扩展方法采用特殊的第一个参数来指定要扩展的原始类型。第一个参数用关键字 this
(它构成了在 C#中的 this
的特殊和独特用途 - 应理解为与使用 this
不同,后者允许引用当前对象实例的成员)。
在以下示例中,要扩展的原始类型是类 string
。String
已通过 Shorten()
方法进行了扩展,该方法提供了缩短的附加功能。已创建静态类 StringExtensions
以保存扩展方法。扩展方法 Shorten()
通过特别标记的第一个参数显示它是 string
的扩展。为了表明 Shorten()
方法扩展 string
,第一个参数用 this
标记。因此,第一个参数的完整签名是 this string text
,其中 string
是扩展的原始类型,text
是所选择的参数名称。
static class StringExtensions
{
public static string Shorten(this string text, int length)
{
return text.Substring(0, length);
}
}
class Program
{
static void Main()
{
// This calls method String.ToUpper()
var myString = "Hello World!".ToUpper();
// This calls the extension method StringExtensions.Shorten()
var newString = myString.Shorten(5);
// It is worth noting that the above call is purely syntactic sugar
// and the assignment below is functionally equivalent
var newString2 = StringExtensions.Shorten(myString, 5);
}
}
作为扩展方法的第一个参数传递的对象 (伴随着 this
关键字)是调用扩展方法的实例。
例如,执行此代码时:
"some string".Shorten(5);
参数的值如下:
text: "some string"
length: 5
请注意,扩展方法仅在与其定义位于同一名称空间中时才可用,如果使用扩展方法由代码显式导入名称空间,或者扩展类是无名称空间。 .NET 框架指南建议将扩展类放在它们自己的命名空间中。但是,这可能会导致发现问题。
这导致扩展方法和正在使用的库之间没有冲突,除非明确地引入可能冲突的名称空间。例如 LINQ Extensions :
using System.Linq; // Allows use of extension methods from the System.Linq namespace
class Program
{
static void Main()
{
var ints = new int[] {1, 2, 3, 4};
// Call Where() extension method from the System.Linq namespace
var even = ints.Where(x => x % 2 == 0);
}
}
从 C#6.0 开始,也可以将 using static
指令放到包含扩展方法的类中。例如,using static System.Linq.Enumerable;
。这使得来自该特定类的扩展方法可用,而无需将来自同一名称空间的其他类型引入范围。
当具有相同签名的类方法可用时,编译器将其优先于扩展方法调用。例如:
class Test
{
public void Hello()
{
Console.WriteLine("From Test");
}
}
static class TestExtensions
{
public static void Hello(this Test test)
{
Console.WriteLine("From extension method");
}
}
class Program
{
static void Main()
{
Test t = new Test();
t.Hello(); // Prints "From Test"
}
}
请注意,如果有两个具有相同签名的扩展函数,并且其中一个在同一名称空间中,那么将优先考虑该名称。另一方面,如果 using
访问了它们,那么编译时错误将随之发生:
以下方法或属性之间的调用不明确
请注意,通过 originalTypeInstance.ExtensionMethod()
调用扩展方法的语法方便是一个可选的便利。该方法也可以以传统方式调用,以便将特殊的第一参数用作该方法的参数。
即,以下两项工作:
//Calling as though method belongs to string--it seamlessly extends string
String s = "Hello World";
s.Shorten(5);
//Calling as a traditional static method with two parameters
StringExtensions.Shorten(s, 5);