扩展方法 - 概述
扩展方法在 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);