技术开发 频道

LINQ项目-支持 LINQ 项目的语言功能


  扩展方法

  λ 表达式是查询体系结构的一个重要部分。扩展方法 是另一个重要部分。扩展方法将动态语言中常见的“快速输入”的灵活性与静态输入语言的性能和编译时验证结合在一起。通过扩展方法,第三方可以使用新方法增加一个类型的公共协定,同时仍然允许单个类型创作者为这些方法提供他们自己的特定实现。

  扩展方法在静态类中定义为静态方法,但在 CLR 元数据中以 [System.Runtime.CompilerServices.Extension] 属性标记。我们鼓励语言为扩展方法提供直接语法。在 C# 中,扩展方法由 this 修饰符指示,该修饰符必须应用于扩展方法的第一个参数。我们来看一下最简单的查询操作符 Where 的定义:
namespace System.Query { using System; using System.Collections.Generic; public static class Sequence { public static IEnumerable Where( this IEnumerable source, Func predicate) { foreach (T item in source) if (predicate(item)) yield return item; } } }
  扩展方法第一个参数的类型指示该扩展应用于哪种类型。在上述示例中,Where 扩展方法将扩展 IEnumerable 类型。由于 Where 是静态方法,因此我们可以像调用任何其他静态方法那样直接调用它:
IEnumerable expr = Sequence.Where(names, s => s.Length < 6);
  但是,扩展方法的特殊之处在于,它们还可以通过实例语法来调用:
IEnumerable expr = names.Where(s => s.Length < 6);
  扩展方法在编译时根据哪些扩展方法在范围内进行解析。当一个命名空间与 C# 的 using 语句或 VB 的 Import 语句一起导入时,由该命名空间的静态类定义的所有扩展方法将导入范围中。

  标准查询操作符将定义为 System.Query.Sequence 类型的扩展方法。在检查标准查询操作符时,您将注意到,除了一个以外,所有操作符都可以定义为 IEnumerable 接口(这个例外是 OfType,我们将在后文加以说明)。这意味着,每个与 IEnumerable 兼容的信息源都可以通过在 C# 中添加以下 using 语句来轻松地获得标准查询操作符:
using System.Query; // makes query operators visible
  希望将标准查询操作符替换为特定类型的用户可以:(a) 使用兼容的签名在特定类型上定义他们自己的同名方法,或者 (b) 定义可扩展特定类型的新的同名扩展方法。希望完全避免标准查询操作符的用户只能将 System.Query 置于范围以外,并为 IEnumerable 编写他们自己的扩展方法。

  对于解析而言,扩展方法具有最低的优先权,并且只有在没有合适的目标类型及其基类型的匹配时才使用。这允许用户定义的类型提供他们自己的、优于标准操作符的查询操作符。例如,请考虑以下自定义集合:
public class MySequence : IEnumerable { public IEnumerator GetEnumerator() { for (int i = 1; i <= 10; i++) yield return i; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerable Where(Func filter) { for (int i = 1; i <= 10; i++) if (filter(i)) yield return i; } }
  假定使用该类定义,以下程序:
MySequence s = new MySequence(); foreach (int item in s.Where(n => n > 3)) Console.WriteLine(item);
  将使用 MySequence.Where 实现,而不是扩展方法,因为实例方法优于扩展方法。

  前面提到的 OfType 操作符是一个无法扩展基于 IEnumerable 的信息源的标准操作符。下面,我们来看一下 OfType 查询操作符:
public static IEnumerable OfType(this IEnumerable source) { foreach (object item in source) if (item is T) yield return (T)item; }
  OfType 不仅接受基于 IEnumerable 的源,还接受针对非参数化 IEnumerable 接口(在 .NET Framework 1.0 版本中提供)编写的源。OfType 操作符允许用户将标准查询操作符应用于以下传统的 .NET 集合:
// "classic" cannot be used directly with query operators IEnumerable classic = new OlderCollectionType(); // "modern" can be used directly with query operators IEnumerablemodern = classic.OfType();
  在本例中,变量 modern 生成了与 classic 相同的值序列,但其类型与现在的 IEnumerable 代码兼容,包括标准查询操作符。

  OfType 操作符对于较新的信息源也很有用,因为它允许根据类型从源筛选值。在生成新序列时,OfType 只省略原始序列中与类型参数不兼容的成员。请考虑下面这个简单的程序,它将从异类数组中提取字符串:
object[] vals = { 1, "Hello", true, "World", 9.1 }; IEnumerable justStrings = vals.OfType();
0
相关文章