如何使用扩展方法
解释扩展方法如何工作以及您如何使用它们。

引言
本文将尝试解释什么是扩展方法以及您如何将它们用于您自己的应用程序。
背景
每个static
辅助类都可以转换为扩展方法,因此只需要具备 .NET 中类的基本知识。
Using the Code
本文使用 Visual Studio 2008。尽管 Visual Studio 2005 也允许您使用 .NET 3.5 功能,但它的智能感知支持并不那么好。对于大多数代码来说,在 Visual Studio 2005 中使用它应该不成问题。
问题
如果您想向现有类添加额外功能,如果幸运的话,并且该类没有被密封,您可以重写它并添加额外的方法。但这并不总是您想要的,有时根本不可能。
以接口为例,IEnumerable<T>
,我们对它了解多少?
MSDN 描述是
公开枚举器,该枚举器支持对指定类型的集合进行简单迭代。
所以每个集合都应该实现这个接口,它是一个列表还是一个数组无关紧要,我们只知道我们可以取回元素。
解决方案
我们声明了一个string
数组来使用。
string[] strings = new string[] { "Green", "Yellow", "Red", "Blue" };
所以假设我们想要这个IEnumerable<T>
的最后一个元素。
在 .NET 2.0 中编写代码来解决这些问题需要static
辅助方法,我们可以用这个简单的辅助方法来解决它
public static class EnumerableHelpers
{
public static T Last<T>(IEnumerable<T> enumerable)
{
if (enumerable == null)
{
throw new ArgumentNullException("enumerable");
}
// We know we can foreach through it
T last = default(T);
foreach (T item in enumerable)
{
last = item;
}
return last;
}
}
我们还可以对IList
添加一些类型检查,但这个示例将始终有效。如果IEnumerable
为空,它将返回 T 的默认值,对于类为null
,对于int
等为0
。
代码也易于使用,并且可以用于任何可枚举类型。
string last = EnumerableHelpers.Last(strings);
编译器知道使用哪个泛型类型,所以我们不必编写 Last<string>
。但是它看起来不如能够键入 strings.Last()
好。
现在我们唯一需要更改的是在第一个参数之前添加扩展方法的特殊关键字,即 this
。而且它必须始终是第一个参数。
public static class EnumerableHelpers
{
public static T Last<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null)
{
throw new ArgumentNullException("enumerable");
}
// We know we can foreach though it
T last = default(T);
foreach (T item in enumerable)
{
last = item;
}
return last;
}
}
通过此更改,编译器知道它可以在任何 IEnumerable<T>
上使用此方法,甚至会为其显示智能感知。

我们的最后一个代码示例现在可以定义为
string last = strings.Last();
这使得它看起来就像是 IEnumerable<string>
的一个方法。
在 .NET 3.5 中的用法
所有这些针对任何 Enumerable 的扩展方法都已经用 .NET 3.5 实现,并且是 LINQ 的基础。
它是一个List
还是一个Array
或其他任何集合都无关紧要。重要的是它支持IEnumerable<T>
接口。
您获得的唯一额外内容是另一种调用它们的全新语法,例如这种
IEnumerable<int> result =
from s in strings where s.Length > 4 select s.Length;
这可以转化为使用扩展方法
IEnumerable<int> result2 =
strings.Where(s => s.Length > 4).Select(s => s.Length);
这里出现的另一个新功能是 lambda 表达式,这里将不进行解释,可能以后再讲。
您可以通过使用匿名委托轻松地将它们重写为更多 .NET 2.0 版本
IEnumerable<int> result3 =
strings.Where(delegate(string s)
{ return s.Length > 4; }).Select(delegate(string s)
{ return s.Length; });
现在我们已经快完成了,我们也可以完全摆脱扩展方法。
IEnumerable<int> result4 =
Enumerable.Select(Enumerable.Where(strings, delegate(string s)
{ return s.Length > 4; }), delegate(string s) { return s.Length; });
如果您看到这个小的 LINQ 查询(只是一个简单的where
和select
)被翻译了什么,您可以想象一下当您开始创建更复杂的查询时它会做什么。
结论
扩展方法非常易于使用,并且在正确使用时会使您的代码更具可读性。它们可以用于任何东西(类、接口、结构、枚举等),这使它们成为一个强大的工具。
包含的源代码具有本文中使用的代码片段以及您可以使用的其他一些扩展方法。
历史
- 2007 年 10 月 25 日:第一个版本