语言集成查询 (LINQ)


全部折叠 代码:C# 语言集成查询 (LINQ) 语言集成查询 (LINQ) 发送反馈意见 语言集成查询 (LINQ) 是 Visual Studio 2008 中的一组功能,可为 C# 和 Visual Basic 语言语法提供强大的查询功 能。LINQ 引入了标准的、易于学习的查询和更新数据模式,可以对其技术进行扩展以支持几乎任何类型的数据存 储。Visual Studio 2008 包含 LINQ 提供程序的程序集,这些程序集支持将 LINQ 与 .NET Framework 集合、SQL Server 数据库、ADO.NET 数据集和 XML 文档一起使用。 本节内容 相关章节 LINQ 简介 简要介绍可编写的各种应用程序,以及使用 LINQ 查询可以解决的各种问题。 C# 中的 LINQ 入门 描述为理解 C# 文档和示例所应了解的基本情况。 Visual Basic 中的 LINQ 入门 描述为理解 Visual Basic 文档和示例所应了解的基本情况。 如何:创建 LINQ 项目 介绍生成 LINQ 项目所需的 .NET Framework 版本、引用和命名空间。 对 LINQ 的 Visual Studio IDE 和工具支持 描述对象关系设计器、对查询的调试器支持以及其他与 LINQ 相关的 IDE 功能。 LINQ 常规编程指南 提供了指向相关主题的链接,这些主题包含有关如何使用 LINQ 进行编程的信息,例如标准查询运算符、表 达式目录树和查询提供程序。 LINQ to Objects 包含指向相关主题的链接,这些主题说明如何使用 LINQ to Objects 来访问内存中的数据结构。 LINQ to XML 包含指向说明如何使用 LINQ to XML 的主题的链接,此功能可提供文档对象模型 (DOM) 的内存中文档修改 功能,并且支持 LINQ 查询表达式。 LINQ to ADO.NET(门户页) 提供 linq_datasetvbtecdlinq 相关文档的入口点。 LINQ to DataSet 使您可以通过使用为其他数据源提供的相同查询功能,在 DataSet 中加入更丰富的查询功能。 LINQ to SQL 为将关系数据作为对象进行管理提供了运行时基础结构。 补充的 LINQ 资源 指向 LINQ 相关信息的其他联机资源的链接。 LINQ to SQL 介绍 LINQ to SQL 技术并提供指向可帮助您使用 LINQ to SQL 的主题的链接。 LINQ to ADO.NET(门户页) 介绍 LINQ to DataSet 技术并提供指向可帮助您使用 LINQ to DataSet 的主题的链接。 LINQ 示例 提供指向相关示例的链接,这些示例用于说明 LINQ 的方方面面。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a73c4aec-5d15-4e98-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 简介 请参见 发送反馈意见 语言集成查询 (LINQ) 是 Visual Studio 2008 和 .NET Framework 3.5 版中一项突破性的创新,它在对象领域和数据 领域之间架起了一座桥梁。 传统上,针对数据的查询都是以简单的字符串表示,而没有编译时类型检查或 IntelliSense 支持。此外,您还必须 针对以下各种数据源学习不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等。LINQ 使查询成为 C# 和 Visual Basic 中的一等语言构造。您可以使用语言关键字和熟悉的运算符针对强类型化对象集合编写查询。下图显示 了一个用 C# 语言编写的、不完整的 LINQ 查询,该查询针对 SQL Server 数据库,并具有完全类型检查和 IntelliSense 支持。 在 Visual Studio 中,可以用 Visual Basic 或 C# 为以下各种数据源编写 LINQ 查询:SQL Server 数据库、XML 文 档、ADO.NET 数据集以及支持 IEnumerable 或泛型 IEnumerable(T) 接口的任意对象集合。此外,还计划了对 ADO.NET Entity Framework 的 LINQ 支持,并且第三方为许多 Web 服务和其他数据库实现编写了 LINQ 提供程序。 LINQ 查询既可在新项目中使用,也可在现有项目中与非 LINQ 查询一起使用。唯一的要求是项目应面向 .NET Framework 3.5 版。 后续步骤 请参见 若要了解有关 LINQ 的更多详细信息,请先根据所选的语言熟悉“入门”部分的一些基本概念: z C# 中的 LINQ 入门 z Visual Basic 中的 LINQ 入门 然后,阅读以下文档以学习您感兴趣的 LINQ 技术: z SQL Server 数据库:LINQ to SQL z XML 文档:LINQ to XML z ADO.NET 数据集:LINQ to DataSet z .NET 集合、文件、字符串等:LINQ to Objects 概念 语言集成查询 (LINQ) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/24dddf19-12a0-4707-... 全部折叠 代码:C# 语言集成查询 (LINQ) C# 中的 LINQ 入门 请参见 发送反馈意见 本节包含可帮助您了解 LINQ 文档和示例的其余内容的基本背景信息。 本节内容 相关章节 请参见 LINQ 查询简介 介绍所有语言和数据源共有的基本 LINQ 查询操作的三个部分。 LINQ 和泛型类型 简单介绍泛型类型在 LINQ 中的使用。 基本查询操作 (LINQ) 介绍最常见的查询操作类型及其在 Visual Basic 和 C# 中的表示方法。 使用 LINQ 进行数据转换 介绍转换在查询中检索到的数据时可采用的各种方式。 查询操作中的类型关系 (LINQ) 介绍如何在 LINQ 查询操作的三个部分中保留和/或转换类型 查询语法与方法语法 (LINQ) 比较方法语法和查询语法这两种 LINQ 查询表示方法。 支持 LINQ 的 C# 3.0 功能 介绍 C# 3.0 中增加的支持 LINQ 的语言构造。 演练:在 C# 中编写查询 (LINQ) 关于创建 C# LINQ 项目、添加简单数据源和执行某些基本查询操作的分步说明。 语言集成查询 (LINQ) 提供指向阐述 LINQ 技术的主题的链接。 LINQ 查询表达式(C# 编程指南) 概述 LINQ 中的查询并提供指向其他资源的链接。 对 LINQ 的 Visual Studio IDE 和工具支持 介绍可在 Visual Studio 环境中用于设计、编码和调试支持 LINQ 的应用程序的工具。 如何:创建 LINQ 项目 介绍创建 LINQ 项目所需的 .NET Framework 版本、命名空间和引用。 LINQ 常规编程指南 包括指向提供特定 LINQ 功能的详细信息的主题的链接。 LINQ 示例 提供指向阐述 LINQ 示例的主题的链接。 概念 语言集成查询 (LINQ) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/b8700c1f-05c9-4380-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 查询简介 请参见 发送反馈意见 查询是一种从数据源检索数据的表达式。查询通常用专门的查询语言来表示。随着时间的推移,人们已经为各种数 据源开发了不同的语言;例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。因此,开发人员不得不针对他们 必须支持的每种数据源或数据格式而学习新的查询语言。LINQ 通过提供一种跨各种数据源和数据格式使用数据的一 致模型,简化了这一情况。在 LINQ 查询中,始终会用到对象。可以使用相同的基本编码模式来查询和转换 XML 文 档、SQL 数据库、ADO.NET 数据集、.NET 集合中的数据以及对其有 LINQ 提供程序可用的任何其他格式的数据。 查询操作的三个部分 所有 LINQ 查询操作都由以下三个不同的操作组成: 1. 获取数据源。 2. 创建查询。 3. 执行查询。 下面的示例演示如何用源代码表示查询操作的三个部分。为了方便起见,此示例将一个整数数组用作数据源; 但其中涉及的概念同样适用于其他数据源。本主题的其余部分也会引用此示例。 下图显示了完整的查询操作。在 LINQ 中,查询的执行与查询本身截然不同;换句话说,如果只是创建查询变 量,则不会检索任何数据。 C# 复制代码 class IntroToLINQ { static void Main() { // The Three Parts of a LINQ Query: // 1. Data source. int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; // 2. Query creation. // numQuery is an IEnumerable var numQuery = from num in numbers where (num % 2) == 0 select num; // 3. Query execution. foreach (int num in numQuery) { Console.Write("{0,1} ", num); } } } 页码,1/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/37895c02-268c-41d5-... 数据源 查询 在上一个示例中,由于数据源是数组,因此它隐式支持泛型 IEnumerable(T) 接口。这一事实意味着该数据源 可以用 LINQ 进行查询。在 foreach 语句中执行查询,而 foreach 要求使用 IEnumerable 或 IEnumerable (T)。支持 IEnumerable(T) 或派生接口(如泛型 IQueryable(T))的类型称为“可查询类型”。 可查询类型不需要进行修改或特殊处理就可以用作 LINQ 数据源。如果源数据还没有作为可查询类型出现在内 存中,则 LINQ 提供程序必须以此方式表示源数据。例如,LINQ to XML 将 XML 文档加载到可查询的 XElement 类型中: 在 LINQ to SQL 中,首先手动或使用 对象关系设计器(O/R 设计器) 在设计时创建对象关系映射。针对这些 对象编写查询,然后由 LINQ to SQL 在运行时处理与数据库的通信。在下面的示例中,Customer 表示数据库 中的特定表,并且 Table 支持派生自 IEnumerable(T) 的泛型 IQueryable(T) 接口。 有关如何创建特定类型的数据源的更多信息,请参见各种 LINQ 提供程序的文档。但基本规则非常简单:LINQ 数据源是支持泛型 IEnumerable(T) 接口或从该接口继承的接口的任意对象。 C# 复制代码 // Create a data source from an XML document. // using System.Xml.Linq; XElement contacts = XElement.Load(@"c:\myContactList.xml"); C# 复制代码 // Create a data source from a SQL Server database. // using System.Data.Linq; DataContext db = new DataContext(@"c:\northwind\northwnd.mdf"); 注意: 支持非泛型 IEnumerable 接口的类型(如 ArrayList)也可用作 LINQ 数据源。有关更多信息,请参见如 何:使用 LINQ 查询 ArrayList。 查询指定要从数据源中检索的信息。查询还可以指定在返回这些信息之前如何对其进行排序、分组和结构化。 查询存储在查询变量中,并用查询表达式进行初始化。为使编写查询的工作变得更加容易,C# 引入了新的查 询语法。 上一个示例中的查询从整数数组中返回所有偶数。该查询表达式包含三个子句:from、where 和 select。 (如果您熟悉 SQL,您会注意到这些子句的顺序与 SQL 中的顺序相反。) from 子句指定数据源,where 子句应用筛选器,select 子句指定返回的元素的类型。LINQ 查询表达式(C# 编程指南)一节中详细讨论了 这些子句和其他查询子句。目前需要注意的是,在 LINQ 中,查询变量本身不执行任何操作并且不返回任何数 据。它只是存储在以后某个时刻执行查询时为生成结果而必需的信息。有关在幕后是如何构建查询的更多信 页码,2/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/37895c02-268c-41d5-... 查询执行 请参见 息,请参见标准查询运算符概述。 注意: 还可以使用方法语法来表示查询。有关更多信息,请参见查询语法与方法语法 (LINQ)。 延迟执行 如前所述,查询变量本身只是存储查询命令。实际的查询执行会延迟到在 foreach 语句中循环访问查询变量 时发生。此概念称为“延迟执行”,下面的示例对此进行了演示: foreach 语句也是检索查询结果的地方。例如,在上一个查询中,迭代变量 num 保存了返回的序列中的每个 值(一次保存一个值)。 由于查询变量本身从不保存查询结果,因此可以根据需要随意执行查询。例如,可以通过一个单独的应用程序 持续更新数据库。在应用程序中,可以创建一个检索最新数据的查询,并可以按某一时间间隔反复执行该查询 以便每次检索不同的结果。 强制立即执行 对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。Count、Max、Average 和 First 就属于此 类查询。由于查询本身必须使用 foreach 以便返回结果,因此这些查询在执行时不使用显式 foreach 语句。 另外还要注意,这些类型的查询返回单个值,而不是 IEnumerable 集合。下面的查询返回源数组中偶数的计 数: 若要强制立即执行任意查询并缓存其结果,可以调用 ToList(TSource) 或 ToArray(TSource) 方法。 此外,还可以通过在紧跟查询表达式之后的位置放置一个 foreach 循环来强制执行查询。但是,通过调用 ToList 或 ToArray,也可以将所有数据缓存在单个集合对象中。 C# 复制代码 // Query execution. foreach (int num in numQuery) { Console.Write("{0,1} ", num); } C# 复制代码 var evenNumQuery = from num in numbers where (num % 2) == 0 select num; int evenNumCount = evenNumQuery.Count(); C# 复制代码 List numQuery2 = (from num in numbers where (num % 2) == 0 select num).ToList(); // or like this: // numQuery3 is still an int[] var numQuery3 = (from num in numbers where (num % 2) == 0 select num).ToArray(); 概念 C# 中的 LINQ 入门 LINQ 示例 O/R 设计器概述 LINQ 查询表达式(C# 编程指南) foreach,in(C# 参考) 查询关键字(C# 参考) 页码,3/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/37895c02-268c-41d5-... 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/37895c02-268c-41d5-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 和泛型类型 请参见 发送反馈意见 LINQ 查询基于泛型类型,在 .NET Framework 的 2.0 版中引入了泛型类型。您无需深入了解泛型即可开始编写查 询。但是,您可能需要了解两个基本概念: 1. 当您创建泛型集合类(如 List(T))的实例时,您将“T”替换为列表将包含的对象的类型。例如,字符串列表 表示为 List,Customer 对象列表表示为 List。泛型列表是强类型的,且提供了比将 其元素存储为 Object 的集合更多的好处。如果您尝试将 Customer 添加到 List,则会在编译时出 现一条错误。泛型集合易于使用的原因是您不必执行运行时类型强制转换。 2. IEnumerable(T) 是一个接口,通过该接口,可以使用 foreach 语句来枚举泛型集合类。泛型集合类支持 IEnumerable(T),就像非泛型集合类(如 ArrayList)支持 IEnumerable。 有关泛型的更多信息,请参见泛型(C# 编程指南)。 LINQ 查询中的 IEnumerable 变量 让编译器处理泛型类型声明 请参见 LINQ 查询变量类型化为 IEnumerable(T) 或派生类型,如 IQueryable(T)。当您看到类型化为 IEnumerable 的查询变量时,这只意味着在执行该查询时,该查询将生成包含零个或多个 Customer 对象的序列。 有关更多信息,请参见查询操作中的类型关系 (LINQ)。 C# 复制代码 IEnumerable customerQuery = from cust in customers where cust.City == "London" select cust; foreach (Customer customer in customerQuery) { Console.WriteLine(customer.LastName + ", " + customer.FirstName); } 如果您愿意,可以使用 var 关键字来避免使用泛型语法。var 关键字指示编译器通过查看在 from 子句中指 定的数据源来推断查询变量的类型。下面的示例生成与上一个示例相同的编译代码: 当变量的类型明显或显式指定嵌套泛型类型(如由组查询生成的那些类型)并不重要时,var 关键字很有用。 通常,我们建议如果您使用 var,应意识到这可能使您的代码更难以让别人理解。有关更多信息,请参见隐式 类型的局部变量(C# 编程指南)。 C# 复制代码 var customerQuery2 = from cust in customers where cust.City == "London" select cust; foreach(var customer in customerQuery2) { Console.WriteLine(customer.LastName + ", " + customer.FirstName); } 概念 C# 中的 LINQ 入门 泛型(C# 编程指南) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/660e3799-25ca-462c-... 全部折叠 代码:C# 语言集成查询 (LINQ) 基本查询操作 (LINQ) 请参见 发送反馈意见 本主题简要介绍 LINQ 查询表达式,以及您在查询中执行的一些典型类型的操作。下面各主题中提供了更详细的信 息: LINQ 查询表达式(C# 编程指南) 标准查询运算符概述 获取数据源 筛选 排序 注意: 如果您已熟悉查询语言(如 SQL 或 XQuery),则您可以跳过本主题的大部分内容。阅读下一节中的“from 子 句”来了解 LINQ 查询表达式中的子句的顺序。 在 LINQ 查询中,第一步是指定数据源。像在大多数编程语言中一样,在 C# 中,必须先声明变量,才能使用 它。在 LINQ 查询中,最先使用 from 子句的目的是引入数据源 (customers) 和范围变量 (cust)。 范围变量类似于 foreach 循环中的迭代变量,但在查询表达式中,实际上不发生迭代。执行查询时,范围变 量将用作对 customers 中的每个后续元素的引用。因为编译器可以推断 cust 的类型,所以您不必显式指定 此类型。其他范围变量可由 let 子句引入。有关更多信息,请参见 let 子句(C# 参考)。 C# 复制代码 //queryAllCustomers is an IEnumerable var queryAllCustomers = from cust in customers select cust; 注意: 对于非泛型数据源(如 ArrayList),必须显式类型化范围变量。有关更多信息,请参见如何:使用 LINQ 查询 ArrayList和 from 子句(C# 参考)。 也许最常用的查询操作是应用布尔表达式形式的筛选器。此筛选器使查询只返回那些表达式结果为 true 的元 素。使用 where 子句生成结果。实际上,筛选器指定从源序列中排除哪些元素。在下面的示例中,只返回那 些地址位于伦敦的 customers。 您可以使用熟悉的 C# 逻辑 AND 和 OR 运算符来根据需要在 where 子句中应用任意数量的筛选表达式。例 如,若要只返回位于“伦敦”AND 姓名为“Devon”的客户,您应编写下面的代码: 若要返回位于伦敦或巴黎的客户,您应编写下面的代码: 有关更多信息,请参见 where 子句(C# 参考)。 C# 复制代码 var queryLondonCustomers = from cust in customers where cust.City == "London" select cust; C# 复制代码 where cust.City=="London" && cust.Name == "Devon" C# 复制代码 where cust.City == "London" || cust.City == "Paris" 通常可以很方便地将返回的数据进行排序。orderby 子句将使返回的序列中的元素按照被排序的类型的默认比 较器进行排序。例如,下面的查询可以扩展为按 Name 属性对结果进行排序。因为 Name 是一个字符串,所以 默认比较器执行从 A 到 Z 的字母排序。 C# 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a7ea3421-1cf4-4df7-8... 分组 联接 选择(投影) 若要按相反顺序(从 Z 到 A)对结果进行排序,请使用 orderby…descending 子句。 有关更多信息,请参见 orderby 子句(C# 参考)。 复制代码 var queryLondonCustomers3 = from cust in customers where cust.City == "London" orderby cust.Name ascending select cust; 使用 group 子句,您可以按指定的键分组结果。例如,您可以指定结果应按 City 分组,以便位于伦敦或巴 黎的所有客户位于各自组中。在本例中,cust.City 是键。 在使用 group 子句结束查询时,结果采用列表的列表形式。列表中的每个元素是一个具有 Key 成员及根据该 键分组的元素列表的对象。在循环访问生成组序列的查询时,您必须使用嵌套的 foreach 循环。外部循环用 于循环访问每个组,内部循环用于循环访问每个组的成员。 如果您必须引用组操作的结果,可以使用 into 关键字来创建可进一步查询的标识符。下面的查询只返回那些 包含两个以上的客户的组: 有关更多信息,请参见 group 子句(C# 参考)。 注意: 在下面的示例中,类型是显式的以更好地说明概念。您也可以对 custQuery、group 和 customer 使用隐 式类型以让编译器确定准确的类型。 C# 复制代码 // queryCustomersByCity is an IEnumerable> var queryCustomersByCity = from cust in customers group cust by cust.City; // customerGroup is an IGrouping foreach (var customerGroup in queryCustomersByCity) { Console.WriteLine(customerGroup.Key); foreach (Customer customer in customerGroup) { Console.WriteLine(" {0}", customer.Name); } } C# 复制代码 // custQuery is an IEnumerable> var custQuery = from cust in customers group cust by cust.City into custGroup where custGroup.Count() > 2 orderby custGroup.Key select custGroup; 联接运算创建数据源中没有显式建模的序列之间的关联。例如,您可以执行联接来查找符合以下条件的所有客 户:位于巴黎,且从位于伦敦的供应商处订购产品。在 LINQ 中,join 子句始终针对对象集合而非直接针对数 据库表运行。在 LINQ 中,您不必像在 SQL 中那样频繁使用 join,因为 LINQ 中的外键在对象模型中表示为 包含项集合的属性。例如,Customer 对象包含 Order 对象的集合。不必执行联接,只需使用点表示法访问订 单: 有关更多信息,请参见 join 子句(C# 参考)。 复制代码 from order in Customer.Orders... 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a7ea3421-1cf4-4df7-8... 请参见 select 子句生成查询结果并指定每个返回的元素的“形状”或类型。例如,您可以指定结果包含的是整个 Customer 对象、仅一个成员、成员的子集,还是某个基于计算或新对象创建的完全不同的结果类型。当 select 子句生成除源元素副本以外的内容时,该操作称为“投影”。使用投影转换数据是 LINQ 查询表达式的 一种强大功能。有关更多信息,请参见使用 LINQ 进行数据转换和 select 子句(C# 参考)。 概念 C# 中的 LINQ 入门 LINQ 查询表达式(C# 编程指南) 查询关键字(C# 参考) 匿名类型(C# 编程指南) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a7ea3421-1cf4-4df7-8... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用 LINQ 进行数据转换 请参见 发送反馈意见 语言集成查询 (LINQ) 不仅可用于检索数据,而是还是一个功能强大的数据转换工具。通过使用 LINQ 查询,您可以将源序列用作输入,并采 用多种方式修改它以创建新输出序列。您可以通过排序和分组来修改序列本身,而不必修改元素本身。但是,LINQ 查询最强大的功能可能在 于它能够创建新类型。这一功能在 select 子句中实现。例如,可以执行下列任务: z 将多个输入序列合并到具有新类型的单个输出序列中。 z 创建其元素只包含源序列中的各个元素的一个或几个属性的输出序列。 z 创建其元素包含对源数据执行的操作结果的输出序列。 z 创建不同格式的输出序列。例如,您可以将 SQL 行或文本文件的数据转换为 XML。 这只是几个示例。当然,可以采用多种方式将这些转换组合在同一查询中。另外,一个查询的输出序列可用作新查询的输入序列。 将多个输入联接到一个输出序列 可以使用 LINQ 查询来创建包含多个输入序列的元素的输出序列。下面的示例演示如何组合两个内存中的数据结构,但组合来自 XML 或 SQL 或数据集源的数据时可应用相同的原则。假定下面两种类类型: 下面的示例演示该查询: C# 复制代码 class Student { public string First { get; set; } public string Last {get; set;} public int ID { get; set; } public string Street { get; set; } public string City { get; set; } public List Scores; } class Teacher { public string First { get; set; } public string Last { get; set; } public int ID { get; set; } public string City { get; set; } } C# 复制代码 class DataTransformations { static void Main() { // Create the first data source. List students = new List() { new Student {First="Svetlana", Last="Omelchenko", ID=111, Street="123 Main Street", City="Seattle", Scores= new List {97, 92, 81, 60}}, new Student {First="Claire", Last="O’Donnell", ID=112, Street="124 Main Street", City="Redmond", Scores= new List {75, 84, 91, 39}}, new Student {First="Sven", Last="Mortensen", ID=113, Street="125 Main Street", City="Lake City", Scores= new List {88, 94, 65, 91}}, }; // Create the second data source. List teachers = new List() { new Teacher {First="Ann", Last="Beebe", ID=945, City = "Seattle"}, new Teacher {First="Alex", Last="Robinson", ID=956, City = "Redmond"}, new Teacher {First="Michiyo", Last="Sato", ID=972, City = "Tacoma"} }; // Create the query. var peopleInSeattle = (from student in students where student.City == "Seattle" select student.Last) .Concat(from teacher in teachers where teacher.City == "Seattle" select teacher.Last); Console.WriteLine("The following students and teachers live in Seattle:"); // Execute the query. foreach (var person in peopleInSeattle) { Console.WriteLine(person); 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/674eae9e-bc72-4a88-... 选择各个源元素的子集 将内存中的对象转换为 XML 有关更多信息,请参见 join 子句(C# 参考)和 select 子句(C# 参考)。 } Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } /* Output: The following students and teachers live in Seattle: Omelchenko Beebe */ 选择源序列中的各个元素的子集有两种主要方法: 1. 若要只选择源元素的一个成员,请使用点运算。在下面的示例中,假定 Customer 对象包含几个公共属性,其中包括名为 City 的字符串。在执行此查询时,此查询将生成字符串输出序列。 2. 若要创建包含源元素的多个属性的元素,可以使用具有命名对象或匿名类型的对象初始值设定项。下面的示例演示如何使用匿名 类型来封装各个 Customer 元素的两个属性: 有关更多信息,请参见对象和集合初始值设定项(C# 编程指南)和匿名类型(C# 编程指南)。 复制代码 var query = from cust in Customers select cust.City; 复制代码 var query = from cust in Customer select new {Name = cust.Name, City = cust.City}; 通过 LINQ 查询,可以轻松地在内存中的数据结构、SQL 数据库、ADO.NET 数据集和 XML 流或文档之间转换数据。下面的示例将内存 中的数据结构中的对象转换为 XML 元素。 此代码生成下面的 XML 输出: C# 复制代码 class XMLTransform { static void Main() { // Create the data source by using a collection initializer. List students = new List() { new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List{97, 92, 81, 60}}, new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List{75, 84, 91, 39}}, new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List{88, 94, 65, 91}}, }; // Create the query. var studentsToXML = new XElement("Root", from student in students let x = String.Format("{0},{1},{2},{3}", student.Scores[0], student.Scores[1], student.Scores[2], student.Scores[3]) select new XElement("student", new XElement("First", student.First), new XElement("Last", student.Last), new XElement("Scores", x) ) // end "student" ); // end "Root" // Execute the query. Console.WriteLine(studentsToXML); // Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } 复制代码 < Root> Svetlana Omelchenko 97,92,81,60 Claire O'Donnell 75,84,91,39 Sven Mortensen 88,94,65,91 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/674eae9e-bc72-4a88-... 对源元素执行操作 请参见 有关更多信息,请参见使用 C# 创建 XML 树 (LINQ to XML)。 输出序列可能不包含源序列的任何元素或元素属性。输出可能是通过将源元素用作输入参数计算出的值的序列。在执行下面这个简单查 询时,此查询会输出一个字符串序列,该序列值表示根据 double 类型的元素的源序列进行的计算。 注意: 如果查询将转换为某个其他域,则不支持在查询表达式中调用方法。例如,不能在 LINQ to SQL 中调用一般 C# 方法,因为 SQL Server 没有该方法的上下文。但是,可以将存储过程映射到方法,然后调用方法。有关更多信息,请参见存储过程 (LINQ to SQL)。 C# 复制代码 class FormatQuery { static void Main() { // Data source. double[] radii = { 1, 2, 3 }; // Query. IEnumerable query = from rad in radii select String.Format("Area = {0}", (rad * rad) * 3.14); // Query execution. foreach (string s in query) Console.WriteLine(s); // Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } /* Output: Area = 3.14 Area = 12.56 Area = 28.26 */ 概念 语言集成查询 (LINQ) LINQ to SQL LINQ to DataSet LINQ to XML LINQ 查询表达式(C# 编程指南) select 子句(C# 参考) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/674eae9e-bc72-4a88-... 全部折叠 代码:C# 语言集成查询 (LINQ) 查询操作中的类型关系 (LINQ) 请参见 发送反馈意见 若要有效编写查询,您应该了解完整的查询操作中的变量类型是如何全部彼此关联的。如果您了解这些关系,就能 够更容易地理解文档中的 LINQ 示例和代码示例。另外,还能了解在使用 var 隐式对变量进行类型化时的后台操 作。 LINQ 查询操作在数据源、查询本身及查询执行中是强类型的。查询中变量的类型必须与数据源中元素的类型和 foreach 语句中迭代变量的类型兼容。此强类型保证在编译时捕获类型错误,以便可以在用户遇到这些错误之前更 正它们。 为了演示这些类型关系,下面的大多数示例对所有变量使用显式类型。最后一个示例演示在您利用使用 var 的隐式 类型时,如何应用相同的原则。 不转换源数据的查询 转换源数据的查询 下图演示不对数据执行转换的 LINQ to Objects 查询操作。源包含一个字符串序列,查询输出也是一个字符串 序列。 1. 数据源的类型参数决定范围变量的类型。 2. 选择的对象的类型决定查询变量的类型。此处的 name 为一个字符串。因此,查询变量是一个 IEnumerable。 3. 在 foreach 语句中循环访问查询变量。因为查询变量是一个字符串序列,所以迭代变量也是一个字符 串。 下图演示对数据执行简单转换的 LINQ to SQL 查询操作。查询将一个 Customer 对象序列用作输入,并只选 择结果中的 Name 属性。因为 Name 是一个字符串,所以查询生成一个字符串序列作为输出。 1. 数据源的类型参数决定范围变量的类型。 2. select 语句返回 Name 属性,而非完整的 Customer 对象。因为 Name 是一个字符串,所以 custNameQuery 的类型参数是 string,而非 Customer。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/99118938-d47c-4d7e-... 让编译器推断类型信息 请参见 3. 因为 custNameQuery 是一个字符串序列,所以 foreach 循环的迭代变量也必须是 string。 下图演示稍微复杂的转换。select 语句返回只捕获原始 Customer 对象的两个成员的匿名类型。 1. 数据源的类型参数始终为查询中的范围变量的类型。 2. 因为 select 语句生成匿名类型,所以必须使用 var 隐式类型化查询变量。 3. 因为查询变量的类型是隐式的,所以 foreach 循环中的迭代变量也必须是隐式的。 虽然您应该了解查询操作中的类型关系,但是您也可以选择让编译器为您执行全部工作。关键字 var 可用于 查询操作中的任何局部变量。下图与前面讨论的第二个示例完全等效。唯一的区别是编译器将为查询操作中的 各个变量提供强类型: 有关 var 的更多信息,请参见隐式类型的局部变量(C# 编程指南)。 概念 C# 中的 LINQ 入门 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/99118938-d47c-4d7e-... 全部折叠 代码:C# 语言集成查询 (LINQ) 查询语法与方法语法 (LINQ) 请参见 发送反馈意见 通过使用 C# 3.0 中引入的声明性查询语法,介绍性 LINQ 文档中的多数查询都被编写为查询表达式。但是,.NET 公共语言运行库 (CLR) 本身并不具有查询语法的概念。因此,在编译时,查询表达式会转换为 CLR 确实了解的内 容:方法调用。这些方法称为“标准查询运算符”,它们具有如下名称:Where、Select、GroupBy、Join、 Max、Average 等。可以通过使用方法语法而非查询语法直接调用这些方法。 通常我们建议使用查询语法,因为它通常更简单、更易读;但是方法语法和查询语法之间并无语义上的区别。此 外,一些查询(如检索匹配指定条件的元素数的那些查询或检索具有源序列中的最大值的元素的查询)只能表示为 方法调用。System.Linq 命名空间中的标准查询运算符的参考文档通常使用方法语法。因此,即使在开始编写 LINQ 查询时,熟悉如何在查询和查询表达式本身中使用方法语法也非常有用。 标准查询运算符扩展方法 下面的示例演示简单的查询表达式和编写为基于方法的查询的语义上等效的查询。 两个示例的输出是相同的。您可以看到两种形式的查询变量的类型是相同的:IEnumerable(T)。 若要了解基于方法的查询,让我们进一步地分析它。注意,在表达式的右侧,where 子句现在表示为对 numbers 对象的实例方法,在您重新调用该对象时其类型为 IEnumerable。如果您熟悉泛型 IEnumerable(T) 接口,那么您就会了解,它不具有 Where 方法。但是,如果您在 Visual Studio IDE 中调用 IntelliSense 完成列表,那么您不仅将看到 Where 方法,而且还会看到许多其他方法,如 Select、 SelectMany、Join 和 Orderby。下面是所有标准查询运算符。 C# 复制代码 class QueryVMethodSyntax { static void Main() { int[] numbers = { 5, 10, 8, 3, 6, 12}; //Query syntax: IEnumerable numQuery1 = from num in numbers where num % 2 == 0 orderby num select num; //Method syntax: IEnumerable numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n); foreach (int i in numQuery1) { Console.Write(i + " "); } Console.WriteLine(System.Environment.NewLine); foreach (int i in numQuery2) { Console.Write(i + " "); } // Keep the console open in debug mode. Console.WriteLine(System.Environment.NewLine); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } } /* Output: 6 8 10 12 6 8 10 12 */ 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/eedd6dd9-fec2-428c-9... Lambda 表达式 查询的组合性 请参见 尽管看起来 IEnumerable(T) 似乎已被重新定义以包括这些附加方法,但事实上并非如此。这些标准查询运算 符作为一种新的方法(称为“扩展方法”)实现。扩展方法可“扩展”现有类型;可如对类型的实例方法一样 调用。标准查询运算符可扩展 IEnumerable(T),这就是您可以编写 numbers.Where(...) 的原因。 若要开始使用 LINQ,您实际需要了解的有关扩展方法的所有内容是,如何通过使用正确的 using 指令将它们 置于您的应用程序中的范围内。您可以在如何:创建 LINQ 项目中进一步了解相关信息。从应用程序的角度来 看,扩展方法和正常的实例方法是相同的。 有关扩展方法的更多信息,请参见扩展方法(C# 编程指南)。有关标准查询运算符的更多信息,请参见 LINQ 常规编程指南和标准查询运算符概述 一些 LINQ 提供程序(如 LINQ to SQL 和 LINQ to XML)除了 IEnumerable(T) 外,还为其他类型实现其自身的标准查询运算符和其他扩展方法。 请注意,在上面的示例中,条件表达式 (num % 2 == 0) 是作为内联参数传递到 Where 方法的:Where(num => num % 2 == 0)。此内联表达式称为 lambda 表达式。将代码编写为匿名方法或泛型委托或表达式目录树 是一种便捷的方法,否则编写起来就要麻烦得多。在 C# 中,=> 是 lambda 运算符,可读为“goes to”。运 算符左侧的 num 是输入变量,与查询表达式中的 num 相对应。编译器可推断 num 的类型,因为它了解 numbers 是泛型 IEnumerable(T) 类型。lambda 表达式与查询语法中的表达式或任何其他 C# 表达式或语句 中的表达式相同;它可以包括方法调用和其他复杂逻辑。“返回值”就是表达式结果。 若要开始使用 LINQ,您不必大量使用 lambda。但是,特定查询只可使用方法语法表示,其中一些查询需要 使用 lambda 表达式。进一步熟悉 lambda 后,您会发现,在 LINQ 工具栏中,它们是强大且灵活的工具。有 关更多信息,请参见 Lambda 表达式(C# 编程指南)。 在上面的代码示例中,OrderBy 方法并非是在对 Where 的调用时使用点运算符调用的。Where 可生成筛选 序列,然后 Orderby 通过对其排序来对该序列进行运算。因为查询会返回 IEnumerable,所以您可通过将 方法调用链接在一起,在方法语法中将这些查询组合起来。这就是在您通过使用查询语法编写查询时编译器在 后台所执行的操作。并且由于查询变量不存储查询的结果,因此您可以随时修改它或将它用作新查询的基础, 即使在执行它后。 概念 C# 中的 LINQ 入门 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/eedd6dd9-fec2-428c-9... 全部折叠 代码:C# 语言集成查询 (LINQ) 支持 LINQ 的 C# 3.0 功能 请参见 发送反馈意见 下面一节介绍 C# 3.0 中的新语言构造。虽然这些新功能在一定程度上都用于 LINQ 查询,但并限于 LINQ,如果认 为有用,在任何情况下都可以使用这些新功能。 查询表达式 隐式类型化变量 (var) 对象和集合初始值设定项 匿名类型 扩展方法 查询表达式使用类似于 SQL 或 XQuery 的声明性语法来查询 IEnumerable 集合。在编译时,查询语法转换为 对 LINQ 提供程序的标准查询运算符扩展方法实现的方法调用。应用程序通过使用 using 指令指定适当的命 名空间来控制范围内的标准查询运算符。下面的查询表达式获取一个字符串数组,按字符串中的第一个字符对 字符串进行分组,然后对各组进行排序。 有关更多信息,请参见 LINQ 查询表达式(C# 编程指南)。 复制代码 var query = from str in stringArray group str by str[0] into stringGroup orderby stringGroup.Key select stringGroup; 不必在声明并初始化变量时显式指定类型,您可以使用 var 修饰符来指示编译器推断并分配类型,如下所 示: 声明为 var 的变量与显式指定其类型的变量一样都是强类型。通过使用 var,可以创建匿名类型,但它可用 于任何局部变量。也可以使用隐式类型声明数组。 有关更多信息,请参见隐式类型的局部变量(C# 编程指南)。 复制代码 var number = 5; var name = "Virginia"; var query = from str in stringArray where str[0] == 'm' select str; 通过对象和集合初始值设定项,初始化对象时无需为对象显式调用构造函数。初始值设定项通常用在将源数据 投影到新数据类型的查询表达式中。假定一个类名为 Customer,具有公共 Name 和 Phone 属性,可以按下 列代码中所示使用对象初始值设定项: 有关更多信息,请参见对象和集合初始值设定项(C# 编程指南)。 复制代码 Customer cust = new Customer {Name = "Mike" ; Phone ={ "555-1212 "}; 匿名类型由编译器构建,且类型名称只可用于编译器。匿名类型提供了一种在查询结果中临时分组一组属性的 方便方法,无需定义单独的命名类型。使用新的表达式和对象初始值设定项初始化匿名类型,如下所示: 有关更多信息,请参见匿名类型(C# 编程指南)。 复制代码 select new {name = cust.Name, phone = cust.Phone}; 扩展方法是一种可与类型关联的静态方法,因此可以像实例方法那样对类型调用它。实际上,此功能使您能够 将新方法“添加”到现有类型,而不会实际修改它们。标准查询运算符是一组扩展方法,它们为实现 IEnumerable(T) 的任何类型提供 LINQ 查询功能。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/524b0078-ebfd-45a7-... Lambda 表达式 自动实现的属性 请参见 有关更多信息,请参见扩展方法(C# 编程指南)。 Lambda 表达式是一种内联函数,该函数使用 => 运算符将输入参数与函数体分离,并且可以在编译时转换为 委托或表达式目录树。在 LINQ 编程中,在您对标准查询运算符进行直接方法调用时,会遇到 lambda 表达 式。 有关更多信息,请参见: z 匿名函数(C# 编程指南) z Lambda 表达式(C# 编程指南) z 表达式目录树 通过自动实现的属性,可以更简明地声明属性。当您如下面的示例中所示声明属性时,编译器将创建一个私有 的匿名支持字段,该字段只能通过属性 getter 和 setter 进行访问。 有关更多信息,请参见自动实现的属性(C# 编程指南)。 复制代码 public string Name {get; set;} 概念 语言集成查询 (LINQ) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/524b0078-ebfd-45a7-... 全部折叠 代码:C# 语言集成查询 (LINQ) 演练:在 C# 中编写查询 (LINQ) 请参见 发送反馈意见 本演练将引导您学习新的 C# 3.0 语言功能,并演示如何使用这些功能来编写 LINQ 查询表达式。完成本演练后,您便可以继续学习您感 兴趣的具体 LINQ 提供程序(如 LINQ to SQL、LINQ to DataSet 或 LINQ to XML)的示例和文档。 先决条件 创建 C# 项目 创建内存中数据源 创建查询 本演练需要 Visual Studio 2008。 有关视频演示,请参见 Video How to: Writing Queries in C# (LINQ)(视频帮助:使用 C# 编写查询 (LINQ))。 创建面向 .NET Framework 3.5 版的 C# 项目 1. 启动 Visual Studio。 2. 在“文件”菜单上指向“新建”,然后单击“项目”。 3. 在“新建项目”对话框的右上角有三个图标。单击左边的图标并确保选中“.NET Framework 3.5 版”。 4. 单击“Visual Studio 已安装的模板”下面的“控制台应用程序”图标。 5. 为您的应用程序输入新的名称或接受默认名称,然后单击“确定”。 6. 请注意,您的项目具有对 System.Core.dll 的引用以及适用于 System.Linq 命名空间的 using 指令。 查询的数据源是 Student 对象的简单列表。每个 Student 记录都有名、姓和表示他们在班级中的测验分数的整数数组。将此代码 复制到您的项目中。请注意下列特性: z Student 类包含自动实现的属性。 z 列表中的每个学生都已使用对象初始值设定项进行初始化。 z 列表本身已使用集合初始值设定项进行初始化。 将在不显式调用任何构造函数和使用显式成员访问的情况下初始化并实例化整个数据结构。有关这些新功能的更多信息,请参见自 动实现的属性(C# 编程指南)和对象和集合初始值设定项(C# 编程指南)。 添加数据源 z 将 Student 类和经过初始化的学生列表添加到您的项目的 Program 类中。 在学生列表中添加新学生 z 将新 Student 添加到 Students 列表中并使用您选择的姓名和测验分数。尝试键入新学生的所有信息,以便更好地了解对象 初始值设定项的语法。 C# 复制代码 public class Student { public string First { get; set; } public string Last { get; set; } public int ID { get; set; } public List Scores; } // Create a data source by using a collection initializer. static List students = new List { new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List {97, 92, 81, 60}}, new Student {First="Claire", Last="O’Donnell", ID=112, Scores= new List {75, 84, 91, 39}}, new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List {88, 94, 65, 91}}, new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List {97, 89, 85, 82}}, new Student {First="Debra", Last="Garcia", ID=115, Scores= new List {35, 72, 91, 70}}, new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List {99, 86, 90, 94}}, new Student {First="Hanying", Last="Feng", ID=117, Scores= new List {93, 92, 80, 87}}, new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List {92, 90, 83, 78}}, new Student {First="Lance", Last="Tucker", ID=119, Scores= new List {68, 79, 88, 92}}, new Student {First="Terry", Last="Adams", ID=120, Scores= new List {99, 82, 81, 79}}, new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List {96, 85, 91, 60}}, new Student {First="Michael", Last="Tucker", ID=122, Scores= new List {94, 92, 91, 91} } }; 创建简单查询 z 在应用程序的 Main 方法中创建一个简单查询,执行该查询时,将生成第一次测验中分数高于 90 分的所有学生的列表。请注 意,由于选择了整个 Student 对象,因此查询的类型是 IEnumerable。虽然代码也可以通过使用 var 关键字来使 用隐式类型,但这里使用了显式类型以便清楚地演示结果。(有关 var 的更多信息,请参见隐式类型的局部变量(C# 编程指 南)。) 另请注意,该查询的范围变量 student 用作对源中每个 Student 的引用,以提供对每个对象的成员访问。 页码,1/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2962a610-419a-4276-... 执行查询 修改查询 C# 复制代码 // Create the query. // studentQuery is an IEnumerable var studentQuery = from student in students where student.Scores[0] > 90 select student; 执行查询 1. 现在编写用于执行查询的 foreach 循环。请注意有关代码的以下事项: z 所返回序列中的每个元素是通过 foreach 循环中的迭代变量来访问的。 z 此变量的类型是 Student,查询变量的类型是兼容的 IEnumerable。 2. 添加此代码后,按 Ctrl + F5 生成并运行该应用程序,然后在“控制台”窗口中查看结果。 添加另一个筛选条件 z 您可以在 where 子句中组合多个布尔条件,以便进一步细化查询。下面的代码添加一个条件,以便查询返回第一个分数高于 90 分并且最后一个分数低于 80 分的那些学生。where 子句应类似于以下代码。 有关更多信息,请参见 where 子句(C# 参考)。 C# 复制代码 // Execute the query. // var could be used here also. foreach (Student student in studentQuery) { Console.WriteLine("{0}, {1}", student.Last, student.First); } 复制代码 where student.Scores[0] > 90 && student.Scores[3] < 80 对结果进行排序 1. 如果结果按某种顺序排列,则浏览结果会更容易。您可以根据源元素中的任何可访问字段对返回的序列进行排序。例如,下 面的 orderby 子句根据每个学生的姓按从 A 到 Z 的字母顺序对结果进行排序。紧靠在 where 语句之后、select 语句之 前,将下面的 orderby 子句添加到您的查询中: 2. 现在更改 orderby 子句,以便根据第一次测验的分数的反向顺序,即从高分到低分的顺序对结果进行排序。 3. 更改 WriteLine 格式字符串以便您可以查看分数: 有关更多信息,请参见 orderby 子句(C# 参考)。 对结果进行分组 1. 分组是查询表达式中的强大功能。包含 group 子句的查询将生成一系列组,每个组本身包含一个 Key 和一个序列,该序列 由该组的所有成员组成。下面的新查询使用学生的姓的第一个字母作为关键字对学生进行分组。 2. 请注意,查询的类型现在已更改。该查询现在生成一系列将 char 类型作为关键字的组,以及一系列 Student 对象。由于 查询的类型已更改,因此下面的代码也会更改 foreach 执行循环: 复制代码 orderby student.Last ascending 复制代码 orderby student.Scores[0] descending 复制代码 Console.WriteLine("{0}, {1} {2}", s.Last, s.First, s.Scores[0]); C# 复制代码 // studentQuery2 is an IEnumerable> var studentQuery2 = from student in students group student by student.Last[0]; C# 复制代码 // studentGroup is a IGrouping foreach (var studentGroup in studentQuery2) { Console.WriteLine(studentGroup.Key); foreach (Student student in studentGroup) { Console.WriteLine(" {0}, {1}", student.Last, student.First); } } 页码,2/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2962a610-419a-4276-... 3. 按 Ctrl + F5 运行该应用程序并在“控制台”窗口中查看结果。 有关更多信息,请参见 group 子句(C# 参考)。 隐式类型化变量 z 显式编写 IGroupings 的 IEnumerables 代码很快就会变得乏味。通过使用 var,您可以更方便地编写相同的查询和 foreach 循环。var 关键字不会更改对象的类型,它仅指示编译器推断类型。将 studentQuery 和迭代变量 group 的类型更 改为 var 并重新运行查询。请注意,在内部 foreach 循环中,迭代变量仍然类型化为 Student,而查询仍可像以前一样工 作。将 s 迭代变量更改为 var 并再次运行查询。您将看到完全相同的结果。 有关 var 的更多信息,请参见隐式类型的局部变量(C# 编程指南)。 按键值对组进行排序 z 当您运行前面的查询时,会注意到组没有按字母顺序排列。若要改变这种情况,您必须在 group 子句后提供 orderby 子句。 但要使用 orderby 子句,您首先需要一个标识符,用作对 group 子句创建的组的引用。可以使用 into 关键字来提供此标识 符,如下所示: 当您运行此查询时,就会看到组现在已按字母顺序排序。 使用 let 引入标识符 z 您可以使用 let 关键字为查询表达式中的任何表达式结果引入标识符。此标识符可以提供方便(如下面的示例所示),也可以 通过存储表达式的结果来避免多次计算,从而提高性能。 有关更多信息,请参见 let 子句(C# 参考)。 在查询表达式中使用方法语法 z 如查询语法与方法语法 (LINQ) 中所述,一些查询操作只能使用方法语法表示。下面的代码计算源序列中每个 Student 的总 分,然后对该查询的结果调用 Average() 方法来计算班级的平均分。请注意,查询表达式的两边使用了括号。 C# 复制代码 var studentQuery3 = from student in students group student by student.Last[0]; foreach (var groupOfStudents in studentQuery3) { Console.WriteLine(groupOfStudents.Key); foreach (var student in groupOfStudents) { Console.WriteLine(" {0}, {1}", student.Last, student.First); } } C# 复制代码 var studentQuery4 = from student in students group student by student.Last[0] into studentGroup orderby studentGroup.Key select studentGroup; foreach (var groupOfStudents in studentQuery4) { Console.WriteLine(groupOfStudents.Key); foreach (var student in groupOfStudents) { Console.WriteLine(" {0}, {1}", student.Last, student.First); } } C# 复制代码 // studentQuery5 is an IEnumerable // This query returns those students whose // first test score was higher than their // average score. var studentQuery5 = from student in students let totalScore = student.Scores[0] + student.Scores[1] + student.Scores[2] + student.Scores[3] where totalScore / 4 < student.Scores[0] select student.Last + " " + student.First; foreach (string s in studentQuery5) { Console.WriteLine(s); } C# 复制代码 var studentQuery6 = from student in students let totalScore = student.Scores[0] + student.Scores[1] + student.Scores[2] + student.Scores[3] select totalScore; 页码,3/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2962a610-419a-4276-... 后续步骤 请参见 在 select 子句中转换或投影 1. 查询生成的序列的元素与源序列中的元素不同,这种情况很常见。删除或注释掉您之前的查询和执行循环,并用下面的代码 替换它。请注意,该查询返回一个字符串(而非 Students)序列,这种情况将反映在 foreach 循环中。 2. 本演练前面的代码指出班级的平均分约为 334 分。若要生成总分高于班级平均分的 Students 及其 Student ID 的序列, 可以在 select 语句中使用匿名类型: double averageScore = studentQuery6.Average(); Console.WriteLine("Class average score = {0}", averageScore); C# 复制代码 IEnumerable studentQuery7 = from student in students where student.Last == "Garcia" select student.First; Console.WriteLine("The Garcias in the class are:"); foreach (string s in studentQuery7) { Console.WriteLine(s); } C# 复制代码 var studentQuery8 = from student in students let x = student.Scores[0] + student.Scores[1] + student.Scores[2] + student.Scores[3] where x > averageScore select new { id = student.ID, score = x }; foreach (var item in studentQuery8) { Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score); } 熟悉了在 C# 中使用查询的基本情况后,便可以开始阅读您感兴趣的具体类型的 LINQ 提供程序的文档和示例: LINQ to SQL LINQ to DataSet LINQ to XML LINQ to Objects LINQ C# 示例 概念 语言集成查询 (LINQ) C# 中的 LINQ 入门 LINQ 查询表达式(C# 编程指南) 补充的 LINQ 资源 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2962a610-419a-4276-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:创建 LINQ 项目 请参见 发送反馈意见 面向 .NET Framework 3.5 版 的新 Visual C# 和 Visual Basic 项目包括基本 LINQ 功能所需的命名空间和引用。只 需创建一个新项目,即可开始编写针对对象集合的 LINQ 查询。Visual Basic 还为 LINQ to XML 功能提供了引用和导 入的命名空间。在 Visual C# 中,必须手动添加这些内容。 若要在任一语言中使用 LINQ to XML 或 LINQ to DataSet,必须手动添加命名空间和引用,如下节所述。 如果要升级用 Visual Studio 早期版本创建的项目,则可能必须手动提供这些内容或其他与 LINQ 相关的引用,还必 须手动将此项目设置为面向 .NET Framework 3.5 版。 添加 LINQ 命名空间和引用的过程 注意: 如果在命令提示符下生成,则必须手动引用驱动器:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5 中与 LINQ 相关的 DLL。 面向 .NET Framework 3.5 版 1. 在 Visual Studio 中,打开一个在 Visual Studio 2005 中创建的 Visual Basic 或 C# 项目,然后按照提 示将它转换为 Visual Studio 2008 项目。 2. 对于 C# 项目,单击“项目”菜单,然后单击“属性”。 a. 在“应用程序”属性页中,选择“目标 Framework”下拉列表中的“.NET Framework 3.5”。 3. 对于 Visual Basic 项目,单击“项目”菜单,然后单击“属性”。 在“编译”属性页中,单击“高级编译选项”,然后在“目标 Framework(所有配置)”下拉列表中选择 “.NET Framework 3.5”。 启用基本 LINQ 功能 1. 在 Visual Basic 或 C# 项目中,单击“项目”菜单,然后单击“添加引用”。 2. 在“添加引用”对话框中单击“.NET”选项卡,滚动到 System.Core.dll 程序集,然后单击它。单击“确 定”。 3. 将 System.Linq 的 using 指令或 Imports 语句添加到您的源代码文件或项目。 有关更多信息,请参见 using 指令(C# 参考)或如何:添加或移除导入的命名空间 (Visual Basic)。 使用表达式目录树启用高级 LINQ 功能 z 如果已引用 System.Core.dll,则添加 System.Linq.Expressions 的 using 指令或 Imports 语句。 有关更多信息,请参见表达式目录树。 使用 LINQ to XML 1. 如有必要,按照本主题中前面介绍的步骤添加对 System.Core.dll 的引用和 System.Linq 的 using 指令 或 Imports 语句。 2. 添加对 System.Xml.Linq 的引用。 3. 添加 System.Xml.Linq 的 using 指令或 Imports 语句。 使用 LINQ to SQL 1. 如有必要,按照本主题中前面介绍的步骤添加对 System.Core.dll 的引用和 System.Linq 的 using 指令 或 Imports 语句。 2. 添加对 System.Data.Linq 的引用。 3. 添加 System.Data.Linq 的 using 指令或 Imports 语句,或者其他 System.Data.Linq 命名空间之一, 具体取决于您特定项目的要求。 有关更多信息,请参见 LINQ to SQL。 使用 LINQ to Dataset 1. 如有必要,按照本主题中前面介绍的步骤添加对 System.Core.dll 的引用和 System.Linq 的 using 指令 或 Imports 语句。 注意: 默认情况下,为 Visual Basic 项目提供此功能。 有关更多信息,请参见 LINQ to XML。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a929e653-09a3-44be-... 请参见 2. 为 LINQ to DataSet 功能添加对 System.Data.DataSetExtensions.dll 的引用。添加对 System.Data.dll 的引用(如果尚不存在)。 3. 为 System.Data 添加 using 指令或 Imports 语句,根据需要也可以为 System.Data.Common、 System.Data.SqlClient 添加,具体取决于您连接到数据库的方式。 有关更多信息,请参见 LINQ to DataSet。 概念 语言集成查询 (LINQ) using 指令(C# 参考) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a929e653-09a3-44be-... 全部折叠 代码:C# 语言集成查询 (LINQ) 对 LINQ 的 Visual Studio IDE 和工具支持 请参见 发送反馈意见 Visual Studio 2008 集成开发环境 (IDE) 提供了下列支持 LINQ 应用程序开发的功能: 对象关系设计器 SQLMetal 命令行工具 支持 LINQ 的代码编辑器 Visual Studio 调试器支持 请参见 对象关系设计器是一种可视化设计工具,可在 LINQ to SQL 应用程序中使用该工具并通过 C# 或 Visual Basic 生成表示基础数据库中的关系数据的类。有关更多信息,请参见对象关系设计器(O/R 设计器)。 SQLMetal 是一种可在生成过程中使用的命令行工具,该工具可从现有数据库生成类以供在 LINQ to SQL 应用 程序中使用。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 C# 和 Visual Basic 代码编辑器通过新的 IntelliSense 和格式设置功能广泛地支持 LINQ。有关更多信息,请参 见 Visual C# 中的新增功能和 Visual Basic 中的新增功能。 Visual Studio 调试器支持调试查询表达式。有关更多信息,请参见调试 LINQ。 概念 语言集成查询 (LINQ) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2bee46c4-bf12-43a5-a... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 常规编程指南 发送反馈意见 本节包含有关如何使用 LINQ 进行编程的信息,包括标准查询运算符、表达式目录树在 LINQ 中的用法以及自定义 LINQ 提供程序。 本节内容 标准查询运算符概述 提供标准查询运算符简介,还提供包含有关各种类型的查询操作的更多信息的主题链接。 LINQ 中的表达式目录树 讨论表达式目录树在 LINQ 中的用法。 启用数据源以进行 LINQ 查询 介绍自定义 LINQ 提供程序和其他扩展 LINQ 的方法。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/609c7a6b-cbdd-429d-... 全部折叠 代码:C# 语言集成查询 (LINQ) 标准查询运算符概述 请参见 发送反馈意见 “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法。大多数这些方法都在序列上运行,其中的序列是一个 对象,其类型实现了 IEnumerable(T) 接口或 IQueryable(T) 接口。标准查询运算符提供了包括筛选、投影、聚合、 排序等功能在内的查询功能。 共有两组 LINQ 标准查询运算符,一组在类型为 IEnumerable(T) 的对象上运行,另一组在类型为 IQueryable(T) 的 对象上运行。构成每组运算符的方法分别是 Enumerable 和 Queryable 类的静态成员。这些方法被定义为作为方法 运行目标的类型的“扩展方法”。 这意味着可以使用静态方法语法或实例方法语法来调用它们。 此外,许多标准查询运算符方法运行所针对的类型不是基于 IEnumerable(T) 或 IQueryable(T) 的类型。Enumerable 类型定义两个此类方法,这些方法都在类型为 IEnumerable 的对象上运行。利用这些方法(Cast(TResult) (IEnumerable) 和 OfType(TResult)(IEnumerable)),您将能够在 LINQ 模式中查询非参数化或非泛型集合。这些方 法通过创建一个强类型的对象集合来实现这一点。Queryable 类定义两个类似的方法(Cast(TResult)(IQueryable) 和 OfType(TResult)(IQueryable)),这些方法在类型为 Queryable 的对象上运行。 各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。返回单一值的方法 (例如 Average 和 Sum)会立即执行。返回序列的方法会延迟查询执行,并返回一个可枚举的对象。 对于在内存中集合上运行的方法(即扩展 IEnumerable(T) 的那些方法),返回的可枚举对象将捕获传递到方法的参 数。在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。 与之相反,扩展 IQueryable(T) 的方法不会实现任何查询行为,但会生成一个表示要执行的查询的表达式目录树。 查询处理由源 IQueryable(T) 对象处理。 可以在一个查询中将对查询方法的调用链接在一起,这就使得查询的复杂性可能会变得不确定。 下面的代码示例演示如何使用标准查询运算符来获取有关序列的信息。 查询表达式语法 C# 复制代码 string sentence = "the quick brown fox jumps over the lazy dog"; // Split the string into individual words to create a collection. string[] words = sentence.Split(' '); // Using query expression syntax. var query = from word in words group word.ToUpper() by word.Length into gr orderby gr.Key select new { Length = gr.Key, Words = gr }; // Using method-based query syntax. var query2 = words. GroupBy(w => w.Length, w => w.ToUpper()). Select(g => new { Length = g.Key, Words = g }). OrderBy(o => o.Length); foreach (var obj in query) { Console.WriteLine("Words of length {0}:", obj.Length); foreach (string word in obj.Words) Console.WriteLine(word); } // This code example produces the following output: // // Words of length 3: // THE // FOX // THE // DOG // Words of length 4: // OVER // LAZY // Words of length 5: // QUICK // BROWN // JUMPS 某些使用更频繁的标准查询运算符具有专用的 C# 和 Visual Basic 语言关键字语法,利用这些语法,将可以在 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/24cda21e-8af8-4632-... 扩展标准查询运算符 相关章节 请参见 “查询表达式”中调用这些运算符。 有关具有专用关键字及其对应语法的标准查询运算符的更多信息,请参 见标准查询运算符的查询表达式语法。 通过创建适合于目标域或技术的特定于域的方法,您可以增大标准查询运算符的集合。也可以用您自己的实现 来替换标准查询运算符,这些实现提供诸如远程计算、查询转换和优化等附加服务。有关示例,请参见 AsEnumerable(TSource)。 通过以下链接可转到一些主题,这些主题基于功能提供有关各种标准查询运算符的附加信息。 对数据进行排序 Set 操作 筛选数据 限定符运算 投影运算 数据分区 联接运算 对数据进行分组 生成运算 相等运算 元素操作 转换数据类型 串联运算 聚合运算 概念 LINQ 查询简介 标准查询运算符的查询表达式语法 扩展方法(C# 编程指南) 扩展方法 (Visual Basic) 参考 Enumerable Queryable 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/24cda21e-8af8-4632-... 全部折叠 代码:C# 语言集成查询 (LINQ) 标准查询运算符的查询表达式语法 请参见 发送反馈意见 某些使用更频繁的标准查询运算符具有专用的 C# 和 Visual Basic 语言关键字语法,利用这些语法,将可以在“查 询表达式”中调用这些运算符。 与“基于方法”的查询表达形式相比,查询表达式是一种不同的、可读性更好的查 询表达形式。 在编译时,查询表达式子句将被转换为对查询方法的调用。 查询表达式语法表 下表列出了具有等效查询表达式子句的标准查询运算符。C# 和 Visual Basic 编程语言没有为相同方法提供专 用查询表达式语法。下表列出了同时适用于这两种语言的语法。 方法 C# 查询表达式语法 Visual Basic 查询表达式语法 All(TSource)(IEnumerable(TSource), Func(TSource, Boolean)) 不适用 Aggregate … In … Into All (…) (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Any(TSource)(IEnumerable(TSource)) 不适用 Aggregate … In … Into Any() (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Average(IEnumerable(Decimal)) 不适用 Aggregate … In … Into Average() (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Cast(TResult)(IEnumerable) 使用显式类型化的范围 变量,例如: from int i in numbers (有关更多信息,请参 见 from 子句(C# 参 考)。) From … As … (有关更多信息,请参见 From 子句 (Visual Basic)。) Count(TSource)(IEnumerable(TSource)) 不适用 Aggregate … In … Into Count () (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Distinct(TSource)(IEnumerable (TSource)) 不适用 Distinct (有关更多信息,请参见 Distinct 子句 (Visual Basic)。) GroupBy() group … by - 或 - group … by … into … (有关更多信息,请参 见 group 子句(C# 参 考)。) Group … By … Into … (有关更多信息,请参见 Group By 子句 (Visual Basic)。) GroupJoin(TOuter, TInner, TKey, TResult)(IEnumerable(TOuter), IEnumerable(TInner), Func(TOuter, TKey), Func(TInner, TKey), Func (TOuter, IEnumerable(TInner), TResult)) join … in … on … equals … into … (有关更多信息,请参 见 join 子句(C# 参 考)。) Group Join … In … On … (有关更多信息,请参见 Group Join 子句 (Visual Basic)。) Join(TOuter, TInner, TKey, TResult) (IEnumerable(TOuter), IEnumerable (TInner), Func(TOuter, TKey), Func join … in … on … equals … From x In …, y In … Where x.a = b.a 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/7c427bbc-450e-44ef-a... (TInner, TKey), Func(TOuter, TInner, TResult)) (有关更多信息,请参 见 join 子句(C# 参 考)。) - 或 - Join … [As …]In … On … (有关更多信息,请参见 Join 子 句 (Visual Basic)。) LongCount(TSource)(IEnumerable (TSource)) 不适用 Aggregate … In … Into LongCount() (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Max(IEnumerable(Decimal)) 不适用 Aggregate … In … Into Max() (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Min(IEnumerable(Decimal)) 不适用 Aggregate … In … Into Min() (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) OrderBy(TSource, TKey)(IEnumerable (TSource), Func(TSource, TKey)) orderby (有关更多信息,请参 见 orderby 子句(C# 参考)。) Order By (有关更多信息,请参见 Order By 子句 (Visual Basic)。) OrderByDescending(TSource, TKey) (IEnumerable(TSource), Func(TSource, TKey)) orderby … descending (有关更多信息,请参 见 orderby 子句(C# 参考)。) Order By … Descending (有关更多信息,请参见 Order By 子句 (Visual Basic)。) Select select (有关更多信息,请参 见 select 子句(C# 参 考)。) Select (有关更多信息,请参见 Select 子句 (Visual Basic)。) SelectMany 多个 from 子句。 (有关更多信息,请参 见 from 子句(C# 参 考)。) 多个 From 子句 (有关更多信息,请参见 From 子句 (Visual Basic)。) Skip(TSource)(IEnumerable(TSource), Int32) 不适用 Skip (有关更多信息,请参见 Skip 子 句 (Visual Basic)。) SkipWhile 不适用 Skip While (有关更多信息,请参见 Skip While 子句 (Visual Basic)。) Sum(IEnumerable(Decimal)) 不适用 Aggregate … In … Into Sum() (有关更多信息,请参见 Aggregate 子句 (Visual Basic)。) Take(TSource)(IEnumerable(TSource), Int32) 不适用 Take (有关更多信息,请参见 Take 子句 (Visual Basic)。) TakeWhile 不适用 Take While (有关更多信息,请参见 Take While 子句 (Visual Basic)。) ThenBy(TSource, TKey) (IOrderedEnumerable(TSource), Func (TSource, TKey)) orderby …, … (有关更多信息,请参 见 orderby 子句(C# 参考)。) Order By …, … (有关更多信息,请参见 Order By 子句 (Visual Basic)。) 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/7c427bbc-450e-44ef-a... 请参见 ThenByDescending(TSource, TKey) (IOrderedEnumerable(TSource), Func (TSource, TKey)) orderby …, … descending (有关更多信息,请参 见 orderby 子句(C# 参考)。) Order By …, … Descending (有关更多信息,请参见 Order By 子句 (Visual Basic)。) Where where (有关更多信息,请参 见 where 子句(C# 参 考)。) Where (有关更多信息,请参见 Where 子句 (Visual Basic)。) 概念 标准查询运算符概述 参考 Enumerable Queryable 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/7c427bbc-450e-44ef-a... 全部折叠 代码:C# 语言集成查询 (LINQ) 对数据进行排序 请参见 发送反馈意见 排序操作按一个或多个属性对序列的元素进行排序。第一个排序条件对元素执行主要排序。通过指定第二个排序条件,可以对各个 主要排序组中的元素进行排序。 下图演示对一个字符序列执行按字母排序操作的结果。 下面一节中列出了对数据进行排序的标准查询运算符方法。 方法 查询表达式语法示例 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 OrderBy 按升 序对 值进 行排 序。 orderby Order By Enumerable.OrderBy Queryable.OrderBy OrderByDescending 按降 序对 值进 行排 序。 orderby … descending Order By … Descending Enumerable.OrderByDescending Queryable.OrderByDescending ThenBy 按升 序执 行次 要排 序。 orderby …, … Order By …, … Enumerable.ThenBy Queryable.ThenBy ThenByDescending 按降 序执 行次 要排 序。 orderby …, … descending Order By …, … Descending Enumerable.ThenByDescending Queryable.ThenByDescending Reverse 颠倒 集合 中的 元素 的顺 序。 不适用。 不适用。 Enumerable.Reverse(TSource) Queryable.Reverse(TSource) 主要排序示例 主要升序排序 下面的示例演示如何在 LINQ 查询中使用 orderby(在 Visual Basic 中为 Order By)子句来按字符串长度对数组中的字符 串进行升序排序。 C# 复制代码 string[] words = { "the", "quick", "brown", "fox", "jumps" }; IEnumerable query = from word in words orderby word.Length select word; foreach (string str in query) Console.WriteLine(str); /* This code produces the following output: the 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/6d76e2d7-b418-49b5-... 主要降序排序 下一个示例演示如何在 LINQ 查询中使用 orderby descending(在 Visual Basic 中为 Order By Descending)子句来按 字符串的第一个字母对字符串进行降序排序。 次要排序示例 次要升序排序 下面的示例演示如何在 LINQ 查询中使用 orderby(在 Visual Basic 中为 Order By)子句来对数组中的字符串执行主要和 次要排序。首先按字符串长度,其次按字符串的第一个字母,对字符串进行升序排序。 次要降序排序 下一个示例演示如何在 LINQ 查询中使用 orderby descending(在 Visual Basic 中为 Order By Descending)子句来按 升序执行主要排序,按降序执行次要排序。首先按字符串长度,其次按字符串的第一个字母,对字符串进行排序。 fox quick brown jumps */ C# 复制代码 string[] words = { "the", "quick", "brown", "fox", "jumps" }; IEnumerable query = from word in words orderby word.Substring(0, 1) descending select word; foreach (string str in query) Console.WriteLine(str); /* This code produces the following output: the quick jumps fox brown */ C# 复制代码 string[] words = { "the", "quick", "brown", "fox", "jumps" }; IEnumerable query = from word in words orderby word.Length, word.Substring(0, 1) select word; foreach (string str in query) Console.WriteLine(str); /* This code produces the following output: fox the brown jumps quick */ C# 复制代码 string[] words = { "the", "quick", "brown", "fox", "jumps" }; IEnumerable query = from word in words orderby word.Length, word.Substring(0, 1) descending select word; foreach (string str in query) Console.WriteLine(str); /* This code produces the following output: the fox quick jumps brown 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/6d76e2d7-b418-49b5-... 有关如何对数据进行排序的更多信息 请参见 */ Topic Location How to: Order the Results of a Join Clause C# Programmer's Reference How to: Sort a Collection Visual Basic Language Reference How to: Sort or Filter Text Data by Any Word or Field (LINQ) Language-Integrated Query (LINQ) orderby clause C# Programmer's Reference orderby 子句 C# 程序员参考 如何:对 Join 子句的结果进行排序 C# 程序员参考 如何:对集合进行排序 Visual Basic 语言参考 如何:按任意词或字段对文本数据进行排序或筛选 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 orderby 子句(C# 参考) Order By 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/6d76e2d7-b418-49b5-... 全部折叠 代码:C# 语言集成查询 (LINQ) Set 操作 请参见 发送反馈意见 LINQ 中的 Set 操作是指根据相同或不同集合(或集)中是否存在等效元素来生成结果集的查询操作。 下面一节中列出了执行 Set 操作的标准查询运算符方法。 方法 比较 Set 操作 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 Distinct 从集合移除 重复值。 不适用。 Distinct Enumerable.Distinct Queryable.Distinct Except 返回差集, 差集是指位 于一个集合 但不位于另 一个集合的 元素。 不适用。 不适用。 Enumerable.Except Queryable.Except Intersect 返回交集, 交集是指同 时出现在两 个集合中的 元素。 不适用。 不适用。 Enumerable.Intersect Queryable.Intersect Union 返回并集, 并集是指位 于两个集合 中任一集合 的唯一的元 素。 不适用。 不适用。 Enumerable.Union Queryable.Union Distinct 下图演示 Enumerable.Distinct 方法对字符序列的行为。返回的序列包含输入序列的唯一元素。 Except 下图演示 Enumerable.Except 的行为。返回的序列只包含位于第一个输入序列但不位于第二个输入序列的元 素。 Intersect 下图演示 Enumerable.Intersect 的行为。返回的序列包含两个输入序列共有的元素。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/93a6e634-7e1a-40a5-... 查询表达式语法示例 有关如何执行 Set 操作的更多信息 请参见 Union 下图演示对两个字符序列执行的联合操作。返回的序列包含两个输入序列的唯一的元素。 下面的示例在 LINQ 查询中使用 Distinct 子句(仅在 Visual Basic 中可用)来返回整数列表中的唯一数字。 Topic Location How to: Combine and Compare String Collections (LINQ) Language-Integrated Query (LINQ) How to: Find the Set Difference Between Two Lists (LINQ) Language-Integrated Query (LINQ) 如何:查找两个列表之间的差集 (LINQ) 语言集成查询 (LINQ) 如何:组合和比较字符串集合 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 Distinct 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/93a6e634-7e1a-40a5-... 全部折叠 代码:C# 语言集成查询 (LINQ) 筛选数据 请参见 发送反馈意见 筛选指将结果集限制为只包含那些满足指定条件的元素的操作。它又称为选择。 下图演示了对字符序列进行筛选的结果。筛选操作的谓词指定字符必须为“A”。 下面一节中列出了执行选择的标准查询运算符方法。 方法 查询表达式语法示例 有关如何筛选数据的更多信息 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 OfType 根据值强制转 换为指定类型 的能力选择 值。 不适用。 不适用。 Enumerable.OfType (TResult) Queryable.OfType (TResult) Where 选择基于谓词 函数的值。 where Where Enumerable.Where Queryable.Where 下面的示例使用 where 子句(在 C# 中)或 Where 子句(在 Visual Basic 中)来从数组中筛选那些具有特 定长度的字符串。 C# 复制代码 string[] words = { "the", "quick", "brown", "fox", "jumps" }; IEnumerable query = from word in words where word.Length == 3 select word; foreach (string str in query) Console.WriteLine(str); /* This code produces the following output: the fox */ Topic Location How to: Dynamically Specify Predicate Filters at Runtime C# Programmer's Reference How to: Filter Query Results Visual Basic Language Reference How to: Query An Assembly's Metadata with Reflection Language-Integrated Query (LINQ) How to: Query for Files with a Specified Attribute or Name Language-Integrated Query (LINQ) How to: Sort or Filter Text Data by Any Word or Field (LINQ) Language-Integrated Query (LINQ) where clause C# Programmer's Reference where 子句 C# 程序员参考 如何:使用反射查询程序集的元数据 语言集成查询 (LINQ) 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/cee88d0f-31aa-4c60-9... 请参见 如何:在运行时动态指定谓词筛选器 C# 程序员参考 如何:按任意词或字段对文本数据进行排序或筛选 (LINQ) 语言集成查询 (LINQ) 如何:查询具有指定属性或名称的文件 语言集成查询 (LINQ) 如何:筛选查询结果 Visual Basic 语言参考 概念 标准查询运算符概述 where 子句(C# 参考) Where 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/cee88d0f-31aa-4c60-9... 全部折叠 代码:C# 语言集成查询 (LINQ) 限定符运算 请参见 发送反馈意见 限定符运算返回一个 Boolean 值,该值指示序列中是否有一些元素满足条件或是否所有元素都满足条件。 下图描述了两个不同源序列上的两个不同限定符运算。第一个运算询问是否有一个或多个元素为字符“A”,结果为 true。第二个运算询问是否所有元素都为字符“A”,结果为 true。 以下部分列出了执行限定符运算的标准查询运算符方法。 方法 查询表达式语法示例 有关如何执行限定运算的更多信息 请参见 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 All 确定是否 序列中的 所有元素 都满足条 件。 不适用。 Aggregate … In … Into All(…) Enumerable.All (TSource) Queryable.All (TSource) Any 确定序列 中是否有 元素满足 条件。 不适用。 Aggregate … In … Into Any() Enumerable.Any Queryable.Any Contains 确定序列 是否包含 指定的元 素。 不适用。 不适用。 Enumerable.Contains Queryable.Contains 下面这些示例将 Visual Basic 中的 Aggregate 子句用作 LINQ 查询中的筛选条件的一部分。 第一个示例使用 Aggregate 子句和 All(TSource) 扩展方法从集合中返回其宠物的年龄全都大于指定年龄的人 员。 第二个示例使用 Aggregate 子句和 Any 扩展方法从集合中返回那些至少有一只宠物的年龄大于指定年龄的 人员。 Topic Location How to: Query for Sentences that Contain a Specified Set of Words (LINQ) Language-Integrated Query (LINQ) 如何:查询包含一组指定单词的句子 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 Aggregate 子句 (Visual Basic) 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/0917704d-c4c0-40aa-... 如何:在运行时动态指定谓词筛选器(C# 编程指南) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/0917704d-c4c0-40aa-... 全部折叠 代码:C# 语言集成查询 (LINQ) 投影运算 请参见 发送反馈意见 投影是指将对象转换为一种新形式的操作,该形式通常只包含那些将随后使用的属性。通过使用投影,您可以构建依据每个对象生成的新 类型。您可以映射属性,并对该属性执行数学函数。还可以在不更改原始对象的情况下映射该对象。 下面一节中列出了执行投影的标准查询运算符方法。 方法 查询表达式语法示例 Select 与 SelectMany 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 Select 映射基于转换函数的值。 select Select Enumerable.Select Queryable.Select SelectMany 映射基于转换函数的值序 列,然后将它们展平为一个 序列。 使用多个 from 子 句 使用多个 From 子句 Enumerable.SelectMany Queryable.SelectMany Select 下面的示例使用 C# 中的 select 子句或 Visual Basic 中的 Select 子句来映射字符串列表中每个字符串的第一个字母。 SelectMany 下面的示例使用多个 from 子句(在 C# 中)或 From 子句(在 Visual Basic 中)来映射字符串列表中每个字符串中的每个单词。 C# 复制代码 List words = new List() { "an", "apple", "a", "day" }; var query = from word in words select word.Substring(0, 1); foreach (string s in query) Console.WriteLine(s); /* This code produces the following output: a a a d */ C# 复制代码 List phrases = new List() { "an apple a day", "the quick brown fox" }; var query = from phrase in phrases from word in phrase.Split(' ') select word; foreach (string s in query) Console.WriteLine(s); /* This code produces the following output: an apple a day the quick brown fox */ Select() 和 SelectMany() 的工作都是依据源值生成一个或多个结果值。Select() 为每个源值生成一个结果值。因此,总体结果是 一个与源集合具有相同元素数目的集合。与之相反,SelectMany() 将生成单一总体结果,其中包含来自每个源值的串联子集合。作 为参数传递到 SelectMany() 的转换函数必须为每个源值返回一个可枚举值序列。然后,SelectMany() 将串联这些可枚举序列以创 建一个大的序列。 下面两个插图演示了这两个方法的操作之间的概念性区别。在每种情况下,假定选择器(转换)函数从每个源值中选择一个由花卉 数据组成的数组。 下图描述 Select() 如何返回一个与源集合具有相同元素数目的集合。 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/fe2c07ed-95ac-4276-8... 下图描述 SelectMany() 如何将中间数组序列串联为一个最终结果值,其中包含每个中间数组中的每个值。 代码示例 下面的示例比较 Select() 和 SelectMany() 的行为。代码将通过从源集合的每个花卉名称列表中提取前两项来创建一个“花束”。 在此示例中,转换函数 Select(TSource, TResult)(IEnumerable(TSource), Func(TSource, TResult)) 使用的“单一值”本身就是一个值 集合。这需要额外的 foreach(Visual Basic 中为 For Each)循环,以便枚举每个子序列中的每个字符串。 C# 复制代码 class Bouquet { public List Flowers { get; set; } } static void SelectVsSelectMany() { List bouquets = new List() { new Bouquet { Flowers = new List { "sunflower", "daisy", "daffodil", "larkspur" }}, new Bouquet{ Flowers = new List { "tulip", "rose", "orchid" }}, new Bouquet{ Flowers = new List { "gladiolis", "lily", "snapdragon", "aster", "protea" }}, new Bouquet{ Flowers = new List { "larkspur", "lilac", "iris", "dahlia" }} }; // *********** Select *********** IEnumerable> query1 = bouquets.Select(bq => bq.Flowers); // ********* SelectMany ********* IEnumerable query2 = bouquets.SelectMany(bq => bq.Flowers); Console.WriteLine("Results by using Select():"); // Note the extra foreach loop here. foreach (IEnumerable collection in query1) foreach (string item in collection) Console.WriteLine(item); Console.WriteLine("\nResults by using SelectMany():"); foreach (string item in query2) Console.WriteLine(item); /* This code produces the following output: Results by using Select(): sunflower daisy daffodil larkspur tulip rose orchid 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/fe2c07ed-95ac-4276-8... 有关如何映射的更多信息 请参见 gladiolis lily snapdragon aster protea larkspur lilac iris dahlia Results by using SelectMany(): sunflower daisy daffodil larkspur tulip rose orchid gladiolis lily snapdragon aster protea larkspur lilac iris dahlia */ } Topic Location How to: Combine Data with Joins Visual Basic Language Reference How to: Populate Object Collections from Multiple Sources (LINQ) Language-Integrated Query (LINQ) How to: Return a LINQ Query Result as a Specific Type Visual Basic Language Reference How to: Split a File Into Many Files by Using Groups (LINQ) Language-Integrated Query (LINQ) select clause C# Programmer's Reference select 子句 C# 程序员参考 如何:从多个源填充对象集合 (LINQ) 语言集成查询 (LINQ) 如何:以特定类型返回 LINQ 查询结果 Visual Basic 语言参考 如何:使用组将一个文件拆分成多个文件 (LINQ) 语言集成查询 (LINQ) 如何:使用联接合并数据 Visual Basic 语言参考 概念 标准查询运算符概述 select 子句(C# 参考) Select 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/fe2c07ed-95ac-4276-8... 全部折叠 代码:C# 语言集成查询 (LINQ) 数据分区 请参见 发送反馈意见 LINQ 中的分区指的是在不重新排列元素的情况下,将输入序列划分为两部分,然后返回其中一个部分的操作。 下图显示对一个字符序列执行三个不同的分区操作的结果。第一个操作返回序列中的前三个元素。第二个操作跳过 前三个元素,返回剩余的元素。第三个操作跳过序列中的前两个元素,返回接下来的三个元素。 下面一节中列出了分区序列的标准查询运算符方法。 运算符 查询表达式语法示例 运算符名称 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 Skip 跳过序 列中的 指定位 置之前 的元 素。 不适用。 Skip Enumerable.Skip (TSource) Queryable.Skip (TSource) SkipWhile 基于谓 词函数 跳过元 素,直 到某元 素不再 满足条 件。 不适用。 Skip While Enumerable.SkipWhile Queryable.SkipWhile Take 提取序 列中的 指定位 置之前 的元 素。 不适用。 Take Enumerable.Take (TSource) Queryable.Take (TSource) TakeWhile 基于谓 词函数 提取元 素,直 到某元 素不再 满足条 件。 不适用。 Take While Enumerable.TakeWhile Queryable.TakeWhile Skip 下面的代码示例在 Visual Basic 中使用 Skip 子句来跳过字符串数组中的前四个字符串,然后返回此数组中的 剩余字符串。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/dbd9eecf-1290-428f-a... 请参见 SkipWhile 下面的代码示例在 Visual Basic 中使用 Skip While 子句来跳过数组中字符串的首字母为“a”的字符串。返回 此数组中的剩余字符串。 Take 下面的代码示例在 Visual Basic 中使用 Take 子句来返回字符串数组中的前两个字符串。 TakeWhile 下面的代码示例在 Visual Basic 中使用 Take While 子句来返回数组中的字符串长度小于或等于 5 的字符 串。 概念 标准查询运算符概述 Skip 子句 (Visual Basic) Skip While 子句 (Visual Basic) Take 子句 (Visual Basic) Take While 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/dbd9eecf-1290-428f-a... 全部折叠 代码:C# 语言集成查询 (LINQ) 联接运算 请参见 发送反馈意见 将两个数据源“联接”就是将一个数据源中的对象与另一个数据源中共享某个公共属性的对象关联起来。 当查询所面向的数据源相互之间具有无法直接领会的关系时,联接就成为一项重要的运算。在面向对象的编程中, 这可能意味着在未建模对象之间进行关联,例如对单向关系进行反向推理。下面是单向关系的一个示例:Customer 类有一个类型为 City 的属性,但 City 类没有作为 Customer 对象集合的属性。如果您具有一个 City 对象列表,并 且要查找每个城市中的所有客户,则可以使用联接运算完成此项查找。 LINQ 框架中提供的联接方法有 Join 和 GroupJoin。这些方法执行同等联接,即根据两个数据源的键是否相等来匹配 这两个数据源的联接。(与此相较,Transact-SQL 支持除“等于”之外的联接运算符,例如,“小于”运算符。) 用关系数据库术语表达,就是说 Join 实现了内部联接,这种联接只返回那些在另一个数据集中具有匹配项的对象。 GroupJoin 方法在关系数据库术语中没有直接的等效项,但它实现了内部联接和左外部联接的超集。左外部联接是这 样一种联接,它返回第一个(左)数据源的每个元素,即使该元素在另一个数据源中没有关联元素。 下图显示了一个概念性视图,其中包含两个集合以及这两个集合中的那些包含在内部联接或左外部联接中的元素。 方法 有关如何执行联接运算的更多信息 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 Join 根据键 选择器 函数联 接两个 序列并 提取值 对。 join … in … on … equals … From x In …, y In … Where x.a = b.a - 或 - Join … [As …]In … On … Enumerable.Join Queryable.Join GroupJoin 根据键 选择器 函数联 接两个 序列, 并对每 个元素 的结果 匹配项 进行分 组。 join … in … on … equals … into … Group Join … In … On … Enumerable.GroupJoin Queryable.GroupJoin Topic Location How to: Join by Using Composite Keys C# Programmer's Reference How to: Join Content from Dissimilar Files (LINQ) Language-Integrated Query (LINQ) How to: Order the Results of a Join Clause C# Programmer's Reference How to: Perform a Grouped Join C# Programmer's Reference How to: Perform a Left Outer Join C# Programmer's Reference 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/442d176d-028c-4beb-... 请参见 How to: Perform an Inner Join C# Programmer's Reference How to: Perform Custom Join Operations C# Programmer's Reference How to: Populate Object Collections from Multiple Sources (LINQ) Language-Integrated Query (LINQ) join clause C# Programmer's Reference join 子句 C# 程序员参考 如何:从多个源填充对象集合 (LINQ) 语言集成查询 (LINQ) 如何:使用组合键进行联接 C# 程序员参考 如何:对 Join 子句的结果进行排序 C# 程序员参考 如何:执行内部联接 C# 程序员参考 如何:执行分组联接 C# 程序员参考 如何:执行左外部联接 C# 程序员参考 如何:执行自定义联接操作 C# 程序员参考 如何:联接不同文件的内容 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 匿名类型(C# 编程指南) 匿名类型 如何:构建联接和叉积查询 (LINQ to SQL) 联接类型 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/442d176d-028c-4beb-... 全部折叠 代码:C# 语言集成查询 (LINQ) 对数据进行分组 请参见 发送反馈意见 分组指将数据放入组中以便每个组中的元素共享公共属性的操作。 下图显示了对字符序列进行分组的结果。每个组的键是字符。 以下部分列出了对数据元素进行分组的标准查询运算符方法。 方法 查询表达式语法示例 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 GroupBy 对共享公 共属性的 元素进行 分组。每 个组都由 一个 IGrouping (TKey, TElement) 对象表 示。 group … by - 或 - group … by … into … Group … By … Into … Enumerable.GroupBy Queryable.GroupBy ToLookup 根据键选 择器函数 将元素插 入到 Lookup (TKey, TElement) (一个一 对多字 典)中。 不适用。 不适用。 Enumerable.ToLookup 下面的代码示例使用 C# 中的 group by 子句或 Visual Basic 中的 Group By 子句,根据列表中的整数是奇 数还是偶数对这些整数进行分组。 C# 复制代码 List numbers = new List() { 35, 44, 200, 84, 3987, 4, 199, 329, 446, 208 }; IEnumerable> query = from number in numbers group number by number % 2; foreach (var group in query) { Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:"); foreach (int i in group) Console.WriteLine(i); } 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/43892bd5-1634-479c-... 有关如何对数据进行分组的更多信息 请参见 /* This code produces the following output: Odd numbers: 35 3987 199 329 Even numbers: 44 200 84 4 446 208 */ Topic Location group clause C# Programmer's Reference group 子句 C# 程序员参考 How to: Group a Group C# Programmer's Reference How to: Group Files by Extension (LINQ) Language-Integrated Query (LINQ) How to: Group Results in Various Ways C# Programmer's Reference How to: Perform a Subquery on a Grouping Operation C# Programmer's Reference How to: Split a File Into Many Files by Using Groups (LINQ) Language-Integrated Query (LINQ) 如何:以各种方式对结果进行分组 C# 程序员参考 如何:使用组将一个文件拆分成多个文件 (LINQ) 语言集成查询 (LINQ) 如何:对分组操作执行子查询 C# 程序员参考 如何:对组进行分组 C# 程序员参考 如何:按扩展名对文件进行分组 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 group 子句(C# 参考) Group By 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/43892bd5-1634-479c-... 全部折叠 代码:C# 语言集成查询 (LINQ) 生成运算 请参见 发送反馈意见 生成是指创建新的值序列。 以下部分列出了执行生成的标准查询运算符方法。 方法 请参见 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 DefaultIfEmpty 将空 集合 替换 为具 有默 认值 的单 一实 例集 合。 不适用。 不适用。 Enumerable.DefaultIfEmpty Queryable.DefaultIfEmpty Empty 返回 空集 合。 不适用。 不适用。 Enumerable.Empty (TResult) Range 生成 包含 数字 序列 的集 合。 不适用。 不适用。 Enumerable.Range Repeat 生成 包含 一个 重复 值的 集 合。 不适用。 不适用。 Enumerable.Repeat (TResult) 概念 标准查询运算符概述 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/0c6b27bc-f2c1-4c31-... 全部折叠 代码:C# 语言集成查询 (LINQ) 相等运算 请参见 发送反馈意见 如果两个序列的对应元素相等且这两个序列具有相同数量的元素,则视这两个序列相等。 方法 有关如何在查询中使用相等运算的更多信息 请参见 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 SequenceEqual 通过 成对 地比 较元 素确 定两 个序 列是 否相 等。 不适用。 不适用。 Enumerable.SequenceEqual Queryable.SequenceEqual Topic Location How to: Compare the Contents of Two Folders (LINQ) Language-Integrated Query (LINQ) 如何:比较两个文件夹的内容 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/c15db8b2-0c88-4590-... 全部折叠 代码:C# 语言集成查询 (LINQ) 元素操作 请参见 发送反馈意见 元素操作从一个序列返回单个特定元素。 下面一节中列出了执行元素操作的标准查询运算符方法。 方法 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 ElementAt 返回 集合 中指 定索 引处 的元 素。 不适用。 不适用。 Enumerable.ElementAt (TSource) Queryable.ElementAt(TSource) ElementAtOrDefault 返回 集合 中指 定索 引处 的元 素; 如果 索引 超出 范 围, 则返 回默 认 值。 不适用。 不适用。 Enumerable.ElementAtOrDefault (TSource) Queryable.ElementAtOrDefault (TSource) First 返回 集合 中的 第一 个元 素或 满足 条件 的第 一个 元 素。 不适用。 不适用。 Enumerable.First Queryable.First FirstOrDefault 返回 集合 中的 第一 个元 素或 满足 条件 的第 一个 元 素。 如果 没有 这样 的元 素, 则返 回默 认 值。 不适用。 不适用。 Enumerable.FirstOrDefault Queryable.FirstOrDefault Queryable.FirstOrDefault (TSource)(IQueryable (TSource)) Last 返回 集合 中的 最后 一个 不适用。 不适用。 Enumerable.Last Queryable.Last 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/55792912-59c7-44db-... 有关如何在查询中使用元素运算符的更多信息 元素 或满 足条 件的 最后 一个 元 素。 LastOrDefault 返回 集合 中的 最后 一个 元素 或满 足条 件的 最后 一个 元 素。 如果 没有 这样 的元 素, 则返 回默 认 值。 不适用。 不适用。 Enumerable.LastOrDefault Queryable.LastOrDefault Single 返回 集合 中的 唯一 元素 或满 足条 件的 唯一 元 素。 不适用。 不适用。 Enumerable.Single Queryable.Single SingleOrDefault 返回 集合 中的 唯一 元素 或满 足条 件的 唯一 元 素。 如果 没有 这样 的元 素或 集合 不是 正好 包含 一个 元 素, 则返 回默 认 值。 不适用。 不适用。 Enumerable.SingleOrDefault Queryable.SingleOrDefault Topic Location How to: Query for the Largest File or Files in a Directory Tree Language-Integrated Query (LINQ) 如何:查询目录树中的一个或多个最大的文件 语言集成查询 (LINQ) 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/55792912-59c7-44db-... 请参见 概念 标准查询运算符概述 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/55792912-59c7-44db-... 全部折叠 代码:C# 语言集成查询 (LINQ) 转换数据类型 请参见 发送反馈意见 转换方法更改输入对象的类型。 LINQ 查询中的转换运算可用于各种应用程序。下面是一些示例: z Enumerable.AsEnumerable(TSource) 方法可用于隐藏类型的标准查询运算符的自定义实现。 z Enumerable.OfType(TResult) 方法可用于启用非参数化集合以进行 LINQ 查询。 z Enumerable.ToArray(TSource)、Enumerable.ToDictionary、Enumerable.ToList(TSource) 和 Enumerable.ToLookup 方法可 用于强制立即执行查询,而非推迟到枚举查询时。 方法 查询表达式语法示例 下表列出了执行数据类型转换的标准查询运算符方法。 此表中名称以“As”开头的转换方法可更改源集合的静态类型但不枚举此源集合。名称以“To”开头的方法可枚举源集合并 将项放入相应的集合类型。 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 AsEnumerable 返回类型化 为 IEnumerable (T) 的输入。 不适用。 不适用。 Enumerable.AsEnumerable (TSource) AsQueryable 将(泛型) IEnumerable 转换为(泛 型) IQueryable。 不适用。 不适用。 Queryable.AsQueryable Cast 将集合的元 素强制转换 为指定类 型。 使用显式类型化的 范围变量。例如: from string str in words From … As … Enumerable.Cast(TResult) Queryable.Cast(TResult) OfType 根据值强制 转换为指定 类型的能力 筛选值。 不适用。 不适用。 Enumerable.OfType (TResult) Queryable.OfType (TResult) ToArray 将集合转换 为数组。此 方法强制执 行查询。 不适用。 不适用。 Enumerable.ToArray (TSource) ToDictionary 根据键选择 器函数将元 素放入 Dictionary (TKey, TValue) 中。 此方法强制 执行查询。 不适用。 不适用。 Enumerable.ToDictionary ToList 将集合转换 为 List(T)。 此方法强制 执行查询。 不适用。 不适用。 Enumerable.ToList (TSource) ToLookup 根据键选择 器函数将元 素放入 Lookup (TKey, TElement) (一对多字 典)中。此 方法强制执 行查询。 不适用。 不适用。 Enumerable.ToLookup 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/d39e96ca-8d4d-4971-... 有关如何转换数据类型的更多信息 请参见 下面的代码示例使用显式类型化的范围变量(在 C# 中)或 From As 子句(在 Visual Basic 中)将类型强制转换为子类 型,然后才访问只具有此子类型的成员。 C# 复制代码 class Plant { public string Name { get; set; } } class CarnivorousPlant : Plant { public string TrapType { get; set; } } static void Cast() { Plant[] plants = new Plant[] { new CarnivorousPlant { Name = "Venus Fly Trap", TrapType = "Snap Trap" }, new CarnivorousPlant { Name = "Pitcher Plant", TrapType = "Pitfall Trap" }, new CarnivorousPlant { Name = "Sundew", TrapType = "Flypaper Trap" }, new CarnivorousPlant { Name = "Waterwheel Plant", TrapType = "Snap Trap" } }; var query = from CarnivorousPlant cPlant in plants where cPlant.TrapType == "Snap Trap" select cPlant; foreach (Plant plant in query) Console.WriteLine(plant.Name); /* This code produces the following output: Venus Fly Trap Waterwheel Plant */ } Topic Location How to: Query an ArrayList with LINQ Language-Integrated Query (LINQ) 如何:使用 LINQ 查询 ArrayList 语言集成查询 (LINQ) 概念 标准查询运算符概述 from 子句(C# 参考) From 子句 (Visual Basic) LINQ 查询表达式(C# 编程指南) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/d39e96ca-8d4d-4971-... 全部折叠 代码:C# 语言集成查询 (LINQ) 串联运算 请参见 发送反馈意见 串联是指将一个序列追加到另一个序列的运算。 下图演示对两个字符序列执行的串联运算。 下面一节中列出了执行串联的标准查询运算符方法。 方法 有关如何在查询中使用串联的更多信息 请参见 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 Concat 串联两个序 列以组成一 个序列。 不适用。 不适用。 Enumerable.Concat (TSource) Queryable.Concat (TSource) Topic Location How to: Combine and Compare String Collections (LINQ) Language-Integrated Query (LINQ) 如何:组合和比较字符串集合 (LINQ) 语言集成查询 (LINQ) 概念 标准查询运算符概述 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/59bdbac0-d6e0-4e5f-... 全部折叠 代码:C# 语言集成查询 (LINQ) 聚合运算 请参见 发送反馈意见 聚合运算从值集合计算单个值。从一个月的日温度值计算日平均温度就是聚合运算的一个示例。 下图显示了对一个数字序列执行两个不同聚合运算的结果。第一个运算对这些数字执行求和。第二个运算返回该序 列中的最大值。 以下部分列出了执行聚合运算的标准查询运算符方法。 方法 方法名 说明 C# 查询表达式语法 Visual Basic 查询表达式语法 更多信息 Aggregate 对集合 值执行 自定义 聚合运 算。 不适用。 不适用。 Enumerable.Aggregate Queryable.Aggregate Average 计算值 集合的 平均 值。 不适用。 Aggregate … In … Into Average() Enumerable.Average Queryable.Average Count 对集合 中的元 素进行 计数, 还可以 仅对满 足某一 谓词函 数的元 素进行 计数。 不适用。 Aggregate … In … Into Count() Enumerable.Count Queryable.Count LongCount 对大型 集合中 的元素 进行计 数,还 可以仅 对满足 某一谓 词函数 的元素 进行计 数。 不适用。 Aggregate … In … Into LongCount() Enumerable.LongCount Queryable.LongCount Max 确定集 合中的 最大 值。 不适用。 Aggregate … In … Into Max() Enumerable.Max Queryable.Max 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/36d97c83-5de5-457d-... 查询表达式语法示例 有关如何执行聚合运算的更多信息 请参见 Min 确定集 合中的 最小 值。 不适用。 Aggregate … In … Into Min() Enumerable.Min Queryable.Min Sum 计算集 合中值 的总 和。 不适用。 Aggregate … In … Into Sum() Enumerable.Sum Queryable.Sum Average 下面的代码示例使用 Visual Basic 中的 Aggregate Into Average 子句计算表示温度的数字数组中的平均温 度。 Count 下面的代码示例使用 Visual Basic 中的 Aggregate Into Count 子句计算数组中大于或等于 80 的值的个数。 LongCount 下面的代码示例使用 Visual Basic 中的 Aggregate Into LongCount 子句计算数组中值的个数。 Max 下面的代码示例使用 Visual Basic 中的 Aggregate Into Max 子句计算表示温度的数字数组中的最高温度。 Min 下面的代码示例使用 Visual Basic 中的 Aggregate Into Min 子句计算表示温度的数字数组中的最低温度。 Sum 下面的代码示例使用 Visual Basic 中的 Aggregate Into Sum 子句从表示费用的值数组中计算费用总额。 Topic Location How to: Compute Column Values in a CSV Text File Language-Integrated Query (LINQ) How to: Count, Sum, or Average Data Visual Basic Language Reference How to: Find the Minimum or Maximum Value in a Query Result Visual Basic Language Reference How to: Query for the Largest File or Files in a Directory Tree Language-Integrated Query (LINQ) How to: Query for the Total Number of Bytes in a Set of Folders (LINQ) Language-Integrated Query (LINQ) 如何:在 CSV 文本文件中计算列值 语言集成查询 (LINQ) 如何:对数据进行计数、求和与求平均值计算 Visual Basic 语言参考 如何:查找查询结果中的最小值或最大值 Visual Basic 语言参考 如何:查询一组文件夹中的总字节数 (LINQ) 语言集成查询 (LINQ) 如何:查询目录树中的一个或多个最大的文件 语言集成查询 (LINQ) 概念 标准查询运算符概述 Aggregate 子句 (Visual Basic) 参考 System.Linq 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/36d97c83-5de5-457d-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 中的表达式目录树 请参见 发送反馈意见 在 LINQ 中,表达式目录树用于表示针对数据源的结构化查询,这些数据源实现 IQueryable(T)。例如,LINQ to SQL 提供程序实现 IQueryable(T) 接口,用于查询关系数据存储。C# 和 Visual Basic 编译器会将针对此类数据源的 查询编译为代码,该代码在运行时将生成一个表达式目录树。然后,查询提供程序可以遍历表达式目录树数据结 构,并将其转换为适合于数据源的查询语言。 表达式目录树还可以用在 LINQ 中,用于表示分配给类型为 Expression(TDelegate) 的变量的 Lambda 表达式。 表达式目录树还可用于创建动态 LINQ 查询。有关更多信息,请参见如何:使用表达式目录树来生成动态查询。如 果要生成 LINQ 提供程序,您也可以使用表达式目录树。有关更多信息,请参见演练:创建 IQueryable LINQ 提供 程序。 请参见 概念 表达式目录树 如何:执行表达式目录树 如何:修改表达式目录树 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/1a2e8e74-4bbc-45ab-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:使用表达式目录树来生成动态查询 示例 请参见 发送反馈意见 本主题描述如何使用表达式目录树来创建动态 LINQ 查询。如果在编译时不知道查询的细节,动态查询将十分有用。例如,应用程序可能会提 供一个用户界面,最终用户可以使用该用户界面指定一个或多个谓词来筛选数据。为了使用 LINQ 进行查询,这种应用程序必须使用表达式目 录树在运行时创建 LINQ 查询。 示例 IQueryable 数据 下面的示例演示如何使用表达式目录树依据 IQueryable 数据源构造一个查询,然后执行该查询。代码将生成一个表达式目录树来表示 以下查询: C# 查询 companies.Where(company => (company.ToLower() == "coho winery" || company.Length > 16)).OrderBy(company => company) Visual Basic 查询 companies.Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16).OrderBy (Function(company) company) System.Linq.Expressions 命名空间中的工厂方法用于创建表达式目录树,这些表达式目录树表示构成总体查询的表达式。表示标准查询 运算符方法调用的表达式将引用这些方法的 Queryable 实现。最终的表达式目录树将传递到 IQueryable 数据源的提供程序的 CreateQuery(TElement)(Expression) 实现,用于创建类型为 IQueryable 的可执行查询。结果是通过枚举查询变量获得的。 C# 复制代码 string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light", "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works", "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders", "Blue Yonder Airlines", "Trey Research", "The Phone Company", "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" }; // The IQueryable data to query. IQueryable queryableData = companies.AsQueryable(); // Compose the expression tree that represents the parameter to the predicate. ParameterExpression pe = Expression.Parameter(typeof(string), "company"); // ***** Where(company => (company.ToLower() == "coho winery" || company.Length > 16)) ***** // Create an expression tree that represents the expression 'company.ToLower() == "coho winery"'. Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes)); Expression right = Expression.Constant("coho winery"); Expression e1 = Expression.Equal(left, right); // Create an expression tree that represents the expression 'company.Length > 16'. left = Expression.Property(pe, typeof(string).GetProperty("Length")); right = Expression.Constant(16, typeof(int)); Expression e2 = Expression.GreaterThan(left, right); // Combine the expression trees to create an expression tree that represents the // expression '(company.ToLower() == "coho winery" || company.Length > 16)'. Expression predicateBody = Expression.OrElse(e1, e2); // Create an expression tree that represents the expression // 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))' MethodCallExpression whereCallExpression = Expression.Call( typeof(Queryable), "Where", new Type[] { queryableData.ElementType }, queryableData.Expression, Expression.Lambda>(predicateBody, new ParameterExpression[] { pe })); // ***** End Where ***** // ***** OrderBy(company => company) ***** // Create an expression tree that represents the expression // 'whereCallExpression.OrderBy(company => company)' MethodCallExpression orderByCallExpression = Expression.Call( typeof(Queryable), "OrderBy", new Type[] { queryableData.ElementType, queryableData.ElementType }, whereCallExpression, Expression.Lambda>(pe, new ParameterExpression[] { pe })); // ***** End OrderBy ***** // Create an executable query from the expression tree. IQueryable results = queryableData.Provider.CreateQuery(orderByCallExpression); // Enumerate the results. foreach (string company in results) Console.WriteLine(company); /* This code produces the following output: Blue Yonder Airlines City Power & Light Coho Winery Consolidated Messenger Graphic Design Institute Humongous Insurance Lucerne Publishing 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/1e37e0cc-eef3-48bb-8... 编译代码 请参见 此代码在传递到 Queryable.Where 方法的谓词中使用固定数量的表达式。但是,您可以编写一个视用户输入而定来合并可变数量谓词 表达式的应用程序。视用户输入而定,您也可以更改在查询中调用的标准查询运算符。 Northwind Traders The Phone Company Wide World Importers */ z 在 Visual Studio 中创建一个新的“控制台应用程序”项目。 z 添加对 System.Core.dll 的引用(如果尚未引用它的话)。 z 包括 System.Linq.Expressions 命名空间。 z 从示例中复制代码,并将其粘贴到 Main 方法 (C#) 或 Main Sub 过程 (Visual Basic) 中。 概念 表达式目录树 LINQ 中的表达式目录树 如何:执行表达式目录树 如何:在运行时动态指定谓词筛选器(C# 编程指南) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/1e37e0cc-eef3-48bb-8... 全部折叠 代码:C# 语言集成查询 (LINQ) 启用数据源以进行 LINQ 查询 请参见 发送反馈意见 可通过多种方式来扩展 LINQ,以便能够在 LINQ 模式中查询任何数据源。数据源可以是数据结构、Web 服务、文件 系统或数据库等。LINQ 模式使客户端可以轻松地查询能够进行 LINQ 查询的数据源,因为查询的语法和模式没有更 改。可通过以下方式将 LINQ 扩展到这些数据源: z 在某个类型中实现 IEnumerable(T) 接口,以使 LINQ to Objects 能够查询该类型。 z 创建可扩展某个类型的标准查询运算符方法(比如 Where 和 Select),以使自定义 LINQ 能够查询该类型。 z 为实现 IQueryable(T) 接口的数据源创建一个提供程序。实现此接口的提供程序以表达式目录树的形式接收 LINQ 查询,提供程序能够以自定义方式(例如以远程方式)执行该查询。 z 为数据源创建一个利用现有 LINQ 技术的提供程序。这种提供程序不仅能够进行查询,而且能够为用户定义的 类型插入、更新和删除操作及映射。 本主题将讨论这些选项。 如何使 LINQ 能够查询您的数据源 IQueryable LINQ 提供程序 请参见 内存中数据 通过两种方式,您可以使 LINQ 能够查询内存中数据。如果数据的类型实现了 IEnumerable(T),您可以使用 LINQ to Objects 来查询数据。如果无法通过实现 IEnumerable(T) 接口来启用类型枚举,您可以在该类型中定 义 LINQ 标准查询运算符方法,或创建可扩展该类型的 LINQ 标准查询运算符方法。标准查询运算符的自定义 实现应使用延迟执行来返回结果。 远程数据 使 LINQ 能够查询远程数据源的最佳选项是实现 IQueryable(T) 接口。但是,这与为数据源扩展提供程序(比 如 LINQ to SQL)有所不同。Visual Studio 2008 中没有用于将现有 LINQ 技术(比如 LINQ to SQL)扩展为其 他数据源类型的提供程序模型。 实现 IQueryable(T) 的 LINQ 提供程序之间的复杂性可能差别很大。本节讨论这些不同程度的复杂性。 复杂性较低的 IQueryable 提供程序可与 Web 服务的单一方法交互,比如在演练:创建 IQueryable LINQ 提 供程序主题中创建的提供程序就是如此。这种类型的提供程序非常具体,因为它需要所处理的查询中有具体信 息。它有封闭的类型系统,可能会公开单一结果类型。大多数查询都是在本地执行的,例如,通过使用标准查 询运算符的 Enumerable 实现来执行。复杂性较低的提供程序可能只会检查表示查询的表达式目录树中的一个 方法调用表达式,并允许在其他地方处理查询的其余逻辑。 中等复杂性的 IQueryable 提供程序可能针对具有部分可表达查询语言的数据源。如果该提供程序针对 Web 服务,它可以与 Web 服务的多个方法进行交互,并基于查询提出的问题选择要调用的方法。与简单提供程序 相比,中等复杂性的提供程序的类型系统较丰富,但它仍然是固定类型系统。例如,提供程序可以公开具有可 遍历的一对多关系的类型,但将不会为用户定义的类型提供映射技术。 复杂的 IQueryable 提供程序(比如 LINQ to SQL 提供程序)可以将完整的 LINQ 查询转换为可表达查询语 言(比如 SQL)。与复杂性较低的提供程序相比,复杂的提供程序更为全面,因为它能在查询中处理各种各 样的问题。它还具有开放的类型系统,因而必须包含广泛的基础结构来映射用户定义的类型。开发复杂的提供 程序需要耗费大量的精力。 概念 演练:创建 IQueryable LINQ 提供程序 标准查询运算符概述 LINQ to Objects 参考 IQueryable(T) IEnumerable(T) Enumerable 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/4b92a4db-553b-4e00-... 全部折叠 代码:C# 语言集成查询 (LINQ) 演练:创建 IQueryable LINQ 提供程序 请参见 发送反馈意见 此高级主题提供了分步说明,介绍如何创建自定义 LINQ 提供程序。完成时,您将能够使用所创建的提供程序依据 TerraServer-USA Web 服务编写 LINQ 查询。 TerraServer-USA Web 服务提供了一个接口,指向美国航空像片的数据库。如果给出部分或全部地点名称,它还会公开一个返回有关美国各地点信息的方法。这个名为 GetPlaceList 的方法是 LINQ 提供程序将调用的方法。提供 程序将使用 Windows Communication Foundation (WCF) 与 Web 服务通信。有关 TerraServer-USA Web 服务的更多信息,请参见 Overview of the TerraServer-USA Web Services(TerraServer-USA Web 服务概述)。 此提供程序是一个相对简单的 IQueryable 提供程序。此提供程序需要所处理的查询中有具体信息,并且它具有封闭类型系统,公开用于表示结果数据的单一类型。此提供程序只会检查表示查询的表达式目录树中的一种类型的方 法调用表达式,也就是最里层的 Where 调用。它将提取必须具有的数据以便从此表达式中查询 Web 服务。然后,它将调用 Web 服务并将返回的数据插入表达式目录树中,替代初始的 IQueryable 数据源。标准查询运算符的 Enumerable 实现将处理查询执行的其余部分。 本主题中的代码示例在 C# 和 Visual Basic 中提供。 本演练阐释以下任务: z 在 Visual Studio 中创建项目。 z 实现 IQueryable LINQ 提供程序所需的接口:IQueryable(T)、IOrderedQueryable(T) 和 IQueryProvider。 z 添加自定义 .NET 类型以表示 Web 服务中的数据。 z 创建一个查询上下文类,以及一个从 Web 服务中获取数据的类。 z 创建一个表达式目录树访问器子类,该子类将查找表示最里层 Queryable.Where 方法调用的表达式。 z 创建一个表达式目录树访问器子类,该子类将从 LINQ 查询中提取要在 Web 服务请求中使用的信息。 z 创建一个表达式目录树访问器子类,该子类将修改表示完整 LINQ 查询的表达式目录树。 z 使用计算器类来部分计算表达式目录树。此步骤是必需的,因为它会将 LINQ 查询中的所有局部变量引用转换为值。 z 创建一个表达式目录树帮助器类,以及一个新的异常类。 z 从包含 LINQ 查询的客户端应用程序中测试 LINQ 提供程序。 z 向 LINQ 提供程序中添加更复杂的查询功能。 先决条件 创建项目 实现必要的接口 您需要以下组件来完成本演练: z Visual Studio 2008 注意: 对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。 在 Visual Studio 中创建项目 1. 在 Visual Studio 中,创建一个新的“类库”应用程序。将项目命名为 LinqToTerraServerProvider。(如果在使用 Visual Basic,请在“解决方案资源管理器”中双击“我的项目”;在“应用程序”选项卡上,将“根 命名空间”框中的文本替换为 VBTerraLinqProvider。) 2. 在“解决方案资源管理器”中,选择“Class1.cs”(或“Class1.vb”)文件,并将其重命名为 QueryableTerraServerData.cs(或 QueryableTerraServerData.vb)。在弹出的对话框中,单击“是”以重命名对代码元素的 所有引用。 您在 Visual Studio 中以“类库”项目的形式创建提供程序,因为可执行客户端应用程序将添加提供程序程序集作为对其项目的引用。 添加对 Web 服务的服务引用 1. 在“解决方案资源管理器”中,右击“LinqToTerraServerProvider”项目,并单击“添加服务引用”。 “添加服务引用”对话框将打开。 2. 在“地址”框中,键入 http://terraserver.microsoft.com/TerraService2.asmx。 3. 在“命名空间”框中,键入 TerraServerReference,然后单击“确定”。 即会添加 TerraServer-USA Web 服务作为服务引用,以便应用程序能够通过 Windows Communication Foundation (WCF) 与 Web 服务通信。通过向项目中添加服务引用,Visual Studio 将生成一个“app.config”文件, 该文件包含 Web 服务的代理和终结点。有关更多信息,请参见 Visual Studio 中的 Windows Communication Foundation 服务简介。 现在有了一个项目,其中包含一个名为“app.config”的文件、一个名为“QueryableTerraServerData.cs”(或“QueryableTerraServerData.vb”)的文件,以及一个名为“TerraServerReference”的服务引用。 若要创建 LINQ 提供程序,至少必须实现 IQueryable(T) 和 IQueryProvider 接口。IQueryable(T) 和 IQueryProvider 是从其他必需接口中派生的;因此,通过实现这两个接口,您将同时实现 LINQ 提供程序需要的其他接 口。 如果要支持诸如 OrderBy 和 ThenBy 等排序查询运算符,您还必须实现 IOrderedQueryable(T) 接口。由于 IOrderedQueryable(T) 派生自 IQueryable(T),您可以在一个类型中同时实现这两种接口,此提供程序将会完成该操 作。 实现 System.Linq.IQueryable`1 和 System.Linq.IOrderedQueryable`1 z 在“QueryableTerraServerData.cs”(或“QueryableTerraServerData.vb”)文件中,添加以下代码。 C# 复制代码 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace LinqToTerraServerProvider { public class QueryableTerraServerData : IOrderedQueryable { #region Constructors /// /// This constructor is called by the client to create the data source. /// public QueryableTerraServerData() { Provider = new TerraServerQueryProvider(); Expression = Expression.Constant(this); } /// /// This constructor is called by Provider.CreateQuery(). /// /// public QueryableTerraServerData(TerraServerQueryProvider provider, Expression expression) { if (provider == null) { throw new ArgumentNullException("provider"); } if (expression == null) { throw new ArgumentNullException("expression"); } if (!typeof(IQueryable).IsAssignableFrom(expression.Type)) { throw new ArgumentOutOfRangeException("expression"); } Provider = provider; Expression = expression; } #endregion #region Properties public IQueryProvider Provider { get; private set; } public Expression Expression { get; private set; } public Type ElementType { get { return typeof(TData); } } #endregion #region Enumerators public IEnumerator GetEnumerator() { return (Provider.Execute>(Expression)).GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return (Provider.Execute(Expression)).GetEnumerator(); 页码,1/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 添加自定义类型以表示结果数据 添加功能以从数据源获取数据 QueryableTerraServerData 类的 IOrderedQueryable(T) 实现将实现三个属性(在 IQueryable 中声明)以及两个枚举方法(在 IEnumerable 和 IEnumerable(T) 中声明)。 此类有两个构造函数。将从客户端应用程序中调用第一个构造函数,以创建编写 LINQ 查询所依据的对象。IQueryProvider 实现中的代码将在提供程序库的内部调用第二个构造函数。 针对类型为 QueryableTerraServerData 的对象调用 GetEnumerator 方法时,将执行该方法表示的查询,并枚举查询的结果。 此代码(除类的名称外)并不特定于此 TerraServer-USA Web 服务提供程序。因此,可以为任何 LINQ 提供程序重用该代码。 实现 System.Linq.IQueryProvider z 将 TerraServerQueryProvider 类添加到项目中。 此类中的查询提供程序代码将实现四个方法,这四个方法是实现 IQueryProvider 接口所必需的。两个 CreateQuery 方法创建与数据源关联的查询。两个 Execute 方法发出此类查询以便执行。 非泛型的 CreateQuery 方法使用反射获取所创建的查询在执行时将返回的序列的元素类型。然后,该方法将使用 Activator 类构造一个用该元素类型构造的新 QueryableTerraServerData 实例,作为其泛型类型参 数。对于非泛型 CreateQuery 方法,调用该方法的结果就好像用正确类型参数调用泛型 CreateQuery 方法一样。 大多数查询执行逻辑都是在您将稍后添加的另一个类中处理的。此功能将在其他地方进行处理,因为它特定于所查询的数据源,而此类中的代码对于任何 LINQ 提供程序都是通用的。若要为其他提供程序使用此代码, 您可能必须更改类的名称以及在其中两个方法中引用的查询上下文类型的名称。 } #endregion } } C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; namespace LinqToTerraServerProvider { public class TerraServerQueryProvider : IQueryProvider { public IQueryable CreateQuery(Expression expression) { Type elementType = TypeSystem.GetElementType(expression.Type); try { return (IQueryable)Activator.CreateInstance(typeof(QueryableTerraServerData<>).MakeGenericType(elementType), new object[] { this, expression }); } catch (System.Reflection.TargetInvocationException tie) { throw tie.InnerException; } } // Queryable's collection-returning standard query operators call this method. public IQueryable CreateQuery(Expression expression) { return new QueryableTerraServerData(this, expression); } public object Execute(Expression expression) { return TerraServerQueryContext.Execute(expression, false); } // Queryable's "single value" standard query operators call this method. // It is also called from QueryableTerraServerData.GetEnumerator(). public TResult Execute(Expression expression) { bool IsEnumerable = (typeof(TResult).Name == "IEnumerable`1"); return (TResult)TerraServerQueryContext.Execute(expression, IsEnumerable); } } } 您将需要一个 .NET 类型来表示从 Web 服务中获取的数据。将在客户端 LINQ 查询中使用此类型以定义查询所需的结果。下面的过程将创建这样的类型。此类型(名为 Place)包含有关单一地理位置(比如城市、公园或湖 泊)的信息。 此代码还包含一个名为 PlaceType 的枚举类型,该类型定义各种类型的地理位置,并用在 Place 类中。 创建自定义结果类型 z 向项目中添加 Place 类和 PlaceType 枚举。 Place 类型的构造函数简化了依据 Web 服务返回的类型创建结果对象的过程。尽管提供程序可以直接返回 Web 服务 API 定义的结果类型,但它将需要客户端应用程序添加对 Web 服务的引用。通过创建新类型作为提 供程序库的一部分,客户端不必知道 Web 服务公开的类型和方法。 C# 复制代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LinqToTerraServerProvider { public class Place { // Properties. public string Name { get; private set; } public string State { get; private set; } public PlaceType PlaceType { get; private set; } // Constructor. internal Place(string name, string state, LinqToTerraServerProvider.TerraServerReference.PlaceType placeType) { Name = name; State = state; PlaceType = (PlaceType)placeType; } } public enum PlaceType { Unknown, AirRailStation, BayGulf, CapePeninsula, CityTown, HillMountain, Island, Lake, OtherLandFeature, OtherWaterFeature, ParkBeach, PointOfInterest, River } } 此提供程序实现假定最里层的 Queryable.Where 调用包含要用于查询 Web 服务的位置信息。最里层的 Queryable.Where 调用是 where 子句(在 Visual Basic 中为 Where 子句)或在 LINQ 查询中首先进行的 Queryable.Where 方法调用,或者是最靠近表示查询的表达式目录树“底部”的调用。 创建查询上下文类 z 将 TerraServerQueryContext 类添加到项目中。 C# 复制代码 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace LinqToTerraServerProvider { class TerraServerQueryContext { // Executes the expression tree that is passed to it. internal static object Execute(Expression expression, bool IsEnumerable) { // The expression must represent a query over the data source. if (!IsQueryOverDataSource(expression)) 页码,2/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 添加表达式目录树访问器类 此类组织执行查询的工作。找到表示最里层 Queryable.Where 调用的表达式后,此代码将检索 Lambda 表达式,该表达式表示传递到 Queryable.Where 的谓词。然后,代码会将谓词表达式传递到要部分计算的方 法,以便将对局部变量的所有引用转换为值。接着,代码将调用一个方法以从谓词中提取请求的位置,并调用另一个方法从 Web 服务中获取结果数据。 在下一步中,此代码将复制表示 LINQ 查询的表达式目录树,并对该表达式目录树进行一个修改。代码将使用表达式目录树访问器子类将应用了最里层查询运算符调用的数据源替换为从 Web 服务中获取的 Place 对象 的具体列表。 将 Place 对象的列表插入表达式目录树之前,将通过调用 AsQueryable 将其类型从 IEnumerable 更改为 IQueryable。必须进行此类型更改,因为在重写表达式目录树时,将重新构造表示最里层查询运算符方法的方 法调用的节点。之所以重新构造该节点,原因是其中一个参数(即它所应用于的数据源)发生了更改。如果任何实参不可赋给它将传递到的方法的对应形参,用于重新构造节点的 Call(Expression, MethodInfo, IEnumerable(Expression)) 方法将引发异常。在这种情况下,Place 对象的 IEnumerable 列表将不可赋给 Queryable.Where 的 IQueryable 参数。因此,其类型将更改为 IQueryable。 通过将其类型更改为 IQueryable,集合还将获取一个由 Provider 属性访问的 IQueryProvider 成员,该成员可创建或执行查询。IQueryable°Place 集合的动态类型为 EnumerableQuery,该类型是 System.Linq API 的内部类型。与此类型关联的查询提供程序通过将 Queryable 标准查询运算符调用替换为等效的 Enumerable 运算符来执行查询,以使查询有效地成为 LINQ to Objects 查询。 TerraServerQueryContext 类中的最终代码将对 Place 对象的 IQueryable 列表调用两个方法之一。如果客户端查询返回可枚举的结果,它将调用 CreateQuery,或者,如果客户端查询返回不可枚举的结果,它将调 用 Execute。 此类中的代码特定于此 TerraServer-USA 提供程序。因此,它将封装在 TerraServerQueryContext 类中,而不是直接插入更通用的 IQueryProvider 实现。 您所创建的提供程序只需要 Queryable.Where 谓词中的信息即可查询 Web 服务。因此,它将使用 LINQ to Objects,通过内部 EnumerableQuery 类型来进行执行 LINQ 查询的工作。使用 LINQ to Objects 执行查询的另 一种方式是:让客户端将要由 LINQ to Objects 执行的查询的一部分包装在 LINQ to Objects 查询中。这可以通过对查询的其余部分调用 AsEnumerable(TSource) 来实现;提供程序需要查询的其余部分来实现特定目的。这种 实现的优势在于自定义提供程序和 LINQ to Objects 之间的工作划分将更为透明。 创建一个类以从 Web 服务中获取数据 z 向项目中添加 WebServiceHelper 类(或 Visual Basic 中的模块)。 此类包含用于从 Web 服务中获取数据的功能。此代码使用一个名为 TerraServiceSoapClient 的类型(由 Windows Communication Foundation (WCF) 为项目自动生成)来调用 Web 服务方法 GetPlaceList。然后, 将每个结果从 Web 服务方法的返回类型转换为提供程序为数据定义的 .NET 类型。 此代码包含两项可增强提供程序库可用性的检查。第一项检查通过将每个查询对 Web 服务所进行调用的总次数限制为五次,从而限制了客户端应用程序等待响应的最长时间。将为客户端查询中指定的每个位置生成一个 Web 服务请求。因此,如果查询包含多于五个位置,提供程序将引发异常。 第二项检查确定 Web 服务返回的结果数是否与它可返回的最大结果数相等。如果结果数是最大数目,那么从 Web 服务返回的结果可能已被截断。提供程序将引发异常,而不是将不完整的列表返回到客户端。 throw new InvalidProgramException("No query over the data source was specified."); // Find the call to Where() and get the lambda expression predicate. InnermostWhereFinder whereFinder = new InnermostWhereFinder(); MethodCallExpression whereExpression = whereFinder.GetInnermostWhere(expression); LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)(whereExpression.Arguments[1])).Operand; // Send the lambda expression through the partial evaluator. lambdaExpression = (LambdaExpression)Evaluator.PartialEval(lambdaExpression); // Get the place name(s) to query the Web service with. LocationFinder lf = new LocationFinder(lambdaExpression.Body); List locations = lf.Locations; if (locations.Count == 0) throw new InvalidQueryException("You must specify at least one place name in your query."); // Call the Web service and get the results. Place[] places = WebServiceHelper.GetPlacesFromTerraServer(locations); // Copy the IEnumerable places to an IQueryable. IQueryable queryablePlaces = places.AsQueryable(); // Copy the expression tree that was passed in, changing only the first // argument of the innermost MethodCallExpression. ExpressionTreeModifier treeCopier = new ExpressionTreeModifier(queryablePlaces); Expression newExpressionTree = treeCopier.CopyAndModify(expression); // This step creates an IQueryable that executes by replacing Queryable methods with Enumerable methods. if (IsEnumerable) return queryablePlaces.Provider.CreateQuery(newExpressionTree); else return queryablePlaces.Provider.Execute(newExpressionTree); } private static bool IsQueryOverDataSource(Expression expression) { // If expression represents an unqueried IQueryable data source instance, // expression is of type ConstantExpression, not MethodCallExpression. return (expression is MethodCallExpression); } } } 注意: 本主题中呈现的提供程序是一个简单的提供程序,它自己能够支持的查询很少。因此,它很大程度上依赖于 LINQ to Objects 来执行查询。复杂的 LINQ 提供程序(比如 LINQ to SQL)可以支持全部查询,而无需将任何 工作交给 LINQ to Objects 来完成。 C# 复制代码 using System; using System.Collections.Generic; using LinqToTerraServerProvider.TerraServerReference; namespace LinqToTerraServerProvider { internal static class WebServiceHelper { private static int numResults = 200; private static bool mustHaveImage = false; internal static Place[] GetPlacesFromTerraServer(List locations) { // Limit the total number of Web service calls. if (locations.Count > 5) throw new InvalidQueryException("This query requires more than five separate calls to the Web service. Please decrease the number of locations in your query."); List allPlaces = new List(); // For each location (i.e. name/state pair), call the Web service method to get data. foreach (string location in locations) { Place[] places = CallGetPlaceListMethod(location); allPlaces.AddRange(places); } return allPlaces.ToArray(); } private static Place[] CallGetPlaceListMethod(string location) { TerraServiceSoapClient client = new TerraServiceSoapClient(); PlaceFacts[] placeFacts = null; try { // Call the Web service method "GetPlaceList". placeFacts = client.GetPlaceList(location, numResults, mustHaveImage); // If there are exactly 'numResults' results, they are probably truncated. if (placeFacts.Length == numResults) throw new Exception("The results have been truncated by the Web service and would not be complete. Please try a different query."); // Create Place objects from the PlaceFacts objects returned by the Web service. Place[] places = new Place[placeFacts.Length]; for (int i = 0; i < placeFacts.Length; i++) { places[i] = new Place( placeFacts[i].Place.City, placeFacts[i].Place.State, placeFacts[i].PlaceTypeId); } // Close the WCF client. client.Close(); return places; } catch (TimeoutException timeoutException) { client.Abort(); throw; } catch (System.ServiceModel.CommunicationException communicationException) { client.Abort(); throw; } } } } 页码,3/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 添加表达式计算器 创建可查找最里层 Where 方法调用表达式的访问器 1. 将 ExpressionVisitor 类添加到项目中。这些代码可在如何:实现表达式目录树访问器中获得。将 using 指令(在 Visual Basic 中为 Imports 语句)添加到以下命名空间的文件中:System.Collections.Generic、 System.Collections.ObjectModel 和 System.Linq.Expressions。 2. 将继承 ExpressionVisitor 类的 InnermostWhereFinder 类添加到项目中。 此类继承基表达式目录树访问器类来执行查找特定表达式的功能。基表达式目录树访问器类设计为专用于涉及遍历表达式目录树的特定任务,可在此类任务中继承基表达式目录树访问器类。派生类将重写 VisitMethodCall 方法以搜寻出表示表达式目录树中最里层 Where 调用的表达式,该表达式目录树表示客户端查询。这个最里层的表达式是提供程序从中提取搜索位置的表达式。 创建可提取数据以查询 Web 服务的访问器 z 将 LocationFinder 类添加到项目中。 此类用于从客户端传递到 Queryable.Where 的谓词中提取位置信息。它派生自基表达式目录树访问器类,并且只会重写 VisitBinary 方法。 表达式目录树访问器基类会将二进制表达式,比如像 place.Name == "Seattle" (在 Visual Basic 中为 place.Name = "Seattle")这样的相等表达式发送到 VisitBinary 方法。在这个重写 VisitBinary 方法 中,如果表达式与可提供位置信息的相等表达式模式匹配,则会提取该信息并将其存储在位置列表中。 此类使用表达式目录树访问器在表达式目录树中查找位置信息,因为访问器可用于遍历和检查表达式目录树。与未使用访问器实现的情况相比,生成的代码更为简洁,而且不容易出错。 在演练的此阶段,提供程序仅支持有限的在查询中提供位置信息的方式。在本主题的后面,您将添加功能以便能够采用更多方式提供位置信息。 创建可修改表达式目录树的访问器 z 将 ExpressionTreeModifier 类添加到项目中。 此类派生自基表达式目录树访问器类,并重写 VisitConstant 方法。在此方法中,它会将应用了最里层标准查询运算符调用的对象替换为 Place 对象的具体列表。 CopyAndModify 方法将调用 Visit 方法的基类实现。必须提供此 CopyAndModify 方法,原因是无法从查询上下文类中直接调用 Visit 方法,即 protected(在 Visual Basic 中为 Protected)。 表达式目录树修饰符类之所以使用表达式目录树访问器,原因是访问器可以遍历、检查和复制表达式目录树。由于此类从基表达式目录树访问器类中派生,因此它只需很少的代码就可执行其功能。 C# 复制代码 using System; using System.Linq.Expressions; namespace LinqToTerraServerProvider { internal class InnermostWhereFinder : ExpressionVisitor { private MethodCallExpression innermostWhereExpression; public MethodCallExpression GetInnermostWhere(Expression expression) { Visit(expression); return innermostWhereExpression; } protected override Expression VisitMethodCall(MethodCallExpression expression) { if (expression.Method.Name == "Where") innermostWhereExpression = expression; Visit(expression.Arguments[0]); return expression; } } } C# 复制代码 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace LinqToTerraServerProvider { internal class LocationFinder : ExpressionVisitor { private Expression expression; private List locations; public LocationFinder(Expression exp) { this.expression = exp; } public List Locations { get { if (locations == null) { locations = new List(); this.Visit(this.expression); } return this.locations; } } protected override Expression VisitBinary(BinaryExpression be) { if (be.NodeType == ExpressionType.Equal) { if (ExpressionTreeHelpers.IsMemberEqualsValueExpression(be, typeof(Place), "Name")) { locations.Add(ExpressionTreeHelpers.GetValueFromEqualsExpression(be, typeof(Place), "Name")); return be; } else if (ExpressionTreeHelpers.IsMemberEqualsValueExpression(be, typeof(Place), "State")) { locations.Add(ExpressionTreeHelpers.GetValueFromEqualsExpression(be, typeof(Place), "State")); return be; } else return base.VisitBinary(be); } else return base.VisitBinary(be); } } } C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; namespace LinqToTerraServerProvider { internal class ExpressionTreeModifier : ExpressionVisitor { private IQueryable queryablePlaces; internal ExpressionTreeModifier(IQueryable places) { this.queryablePlaces = places; } internal Expression CopyAndModify(Expression expression) { return this.Visit(expression); } protected override Expression VisitConstant(ConstantExpression c) { // Replace the constant QueryableTerraServerData arg with the queryable Place collection. if (c.Type == typeof(QueryableTerraServerData)) return Expression.Constant(this.queryablePlaces); else return c; } } } 在客户端查询中传递到 Queryable.Where 方法的谓词可能包含不依赖于 Lambda 表达式参数的子表达式。可以并且应当立即计算这些独立的子表达式。它们可能是对必须转换为值的局部变量或成员变量的引用。 下一个类公开 PartialEval(Expression) 方法,该方法确定可以立即计算表达式中的哪些子树(如果有)。然后,它将通过创建 Lambda 表达式、编译该表达式并调用返回的委托来计算那些表达式。最后,它将子树替换 为一个表示常量值的新节点。这被称作部分计算。 页码,4/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 添加帮助器类 添加一个类以执行表达式目录树的部分计算 z 将 Evaluator 类添加到项目中。 C# 复制代码 using System; using System.Collections.Generic; using System.Linq.Expressions; namespace LinqToTerraServerProvider { public static class Evaluator { /// /// Performs evaluation & replacement of independent sub-trees /// /// The root of the expression tree. /// A function that decides whether a given expression node can be part of the local function. /// A new tree with sub-trees evaluated and replaced. public static Expression PartialEval(Expression expression, Func fnCanBeEvaluated) { return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression); } /// /// Performs evaluation & replacement of independent sub-trees /// /// The root of the expression tree. /// A new tree with sub-trees evaluated and replaced. public static Expression PartialEval(Expression expression) { return PartialEval(expression, Evaluator.CanBeEvaluatedLocally); } private static bool CanBeEvaluatedLocally(Expression expression) { return expression.NodeType != ExpressionType.Parameter; } /// /// Evaluates & replaces sub-trees when first candidate is reached (top-down) /// class SubtreeEvaluator : ExpressionVisitor { HashSet candidates; internal SubtreeEvaluator(HashSet candidates) { this.candidates = candidates; } internal Expression Eval(Expression exp) { return this.Visit(exp); } protected override Expression Visit(Expression exp) { if (exp == null) { return null; } if (this.candidates.Contains(exp)) { return this.Evaluate(exp); } return base.Visit(exp); } private Expression Evaluate(Expression e) { if (e.NodeType == ExpressionType.Constant) { return e; } LambdaExpression lambda = Expression.Lambda(e); Delegate fn = lambda.Compile(); return Expression.Constant(fn.DynamicInvoke(null), e.Type); } } /// /// Performs bottom-up analysis to determine which nodes can possibly /// be part of an evaluated sub-tree. /// class Nominator : ExpressionVisitor { Func fnCanBeEvaluated; HashSet candidates; bool cannotBeEvaluated; internal Nominator(Func fnCanBeEvaluated) { this.fnCanBeEvaluated = fnCanBeEvaluated; } internal HashSet Nominate(Expression expression) { this.candidates = new HashSet(); this.Visit(expression); return this.candidates; } protected override Expression Visit(Expression expression) { if (expression != null) { bool saveCannotBeEvaluated = this.cannotBeEvaluated; this.cannotBeEvaluated = false; base.Visit(expression); if (!this.cannotBeEvaluated) { if (this.fnCanBeEvaluated(expression)) { this.candidates.Add(expression); } else { this.cannotBeEvaluated = true; } } this.cannotBeEvaluated |= saveCannotBeEvaluated; } return expression; } } } } 本节包含提供程序的三个帮助器类的代码。 添加由 System.Linq.IQueryProvider 实现使用的帮助器类 z 向项目中添加 TypeSystem 类(或 Visual Basic 中的模块)。 C# 复制代码 using System; using System.Collections.Generic; namespace LinqToTerraServerProvider { internal static class TypeSystem { internal static Type GetElementType(Type seqType) { Type ienum = FindIEnumerable(seqType); if (ienum == null) return seqType; return ienum.GetGenericArguments()[0]; } 页码,5/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 您之前添加的 IQueryProvider 实现将使用此帮助器类。 TypeSystem.GetElementType 使用反射来获取 IEnumerable(Visual Basic 中为 IEnumerable(Of T))集合的泛型类型参数。将从查询提供程序实现的非泛型 CreateQuery 方法中调用此方法,以提供查询结果 集合的元素类型。 此帮助器类并不特定于此 TerraServer-USA Web 服务提供程序。因此,可以为任何 LINQ 提供程序重用该类。 创建表达式目录树帮助器类 z 将 ExpressionTreeHelpers 类添加到项目中。 此类包含一些方法,可使用这些方法来确定有关特定类型的表达式目录树的信息,以及从这些表达式目录树中提取数据。在此提供程序中,LocationFinder 类使用这些方法从表示查询的表达式目录树中提取位置信 息。 为无效查询添加异常类型 z 将 InvalidQueryException 类添加到项目中。 此类定义一个 Exception 类型,当提供程序无法识别来自客户端的 LINQ 查询时,它将引发该类型。通过定义此无效查询异常类型,提供程序可以从代码中的不同位置引发更具体的异常,而不只是 Exception。 private static Type FindIEnumerable(Type seqType) { if (seqType == null || seqType == typeof(string)) return null; if (seqType.IsArray) return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType()); if (seqType.IsGenericType) { foreach (Type arg in seqType.GetGenericArguments()) { Type ienum = typeof(IEnumerable<>).MakeGenericType(arg); if (ienum.IsAssignableFrom(seqType)) { return ienum; } } } Type[] ifaces = seqType.GetInterfaces(); if (ifaces != null && ifaces.Length > 0) { foreach (Type iface in ifaces) { Type ienum = FindIEnumerable(iface); if (ienum != null) return ienum; } } if (seqType.BaseType != null && seqType.BaseType != typeof(object)) { return FindIEnumerable(seqType.BaseType); } return null; } } } C# 复制代码 using System; using System.Linq.Expressions; namespace LinqToTerraServerProvider { internal class ExpressionTreeHelpers { internal static bool IsMemberEqualsValueExpression(Expression exp, Type declaringType, string memberName) { if (exp.NodeType != ExpressionType.Equal) return false; BinaryExpression be = (BinaryExpression)exp; // Assert. if (ExpressionTreeHelpers.IsSpecificMemberExpression(be.Left, declaringType, memberName) && ExpressionTreeHelpers.IsSpecificMemberExpression(be.Right, declaringType, memberName)) throw new Exception("Cannot have 'member' == 'member' in an expression!"); return (ExpressionTreeHelpers.IsSpecificMemberExpression(be.Left, declaringType, memberName) || ExpressionTreeHelpers.IsSpecificMemberExpression(be.Right, declaringType, memberName)); } internal static bool IsSpecificMemberExpression(Expression exp, Type declaringType, string memberName) { return ((exp is MemberExpression) && (((MemberExpression)exp).Member.DeclaringType == declaringType) && (((MemberExpression)exp).Member.Name == memberName)); } internal static string GetValueFromEqualsExpression(BinaryExpression be, Type memberDeclaringType, string memberName) { if (be.NodeType != ExpressionType.Equal) throw new Exception("There is a bug in this program."); if (be.Left.NodeType == ExpressionType.MemberAccess) { MemberExpression me = (MemberExpression)be.Left; if (me.Member.DeclaringType == memberDeclaringType && me.Member.Name == memberName) { return GetValueFromExpression(be.Right); } } else if (be.Right.NodeType == ExpressionType.MemberAccess) { MemberExpression me = (MemberExpression)be.Right; if (me.Member.DeclaringType == memberDeclaringType && me.Member.Name == memberName) { return GetValueFromExpression(be.Left); } } // We should have returned by now. throw new Exception("There is a bug in this program."); } internal static string GetValueFromExpression(Expression expression) { if (expression.NodeType == ExpressionType.Constant) return (string)(((ConstantExpression)expression).Value); else throw new InvalidQueryException( String.Format("The expression type {0} is not supported to obtain a value.", expression.NodeType)); } } } C# 复制代码 using System; namespace LinqToTerraServerProvider { class InvalidQueryException : System.Exception { private string message; public InvalidQueryException(string message) { this.message = message + " "; } public override string Message { get { return "The client query is invalid: " + message; } } } } 页码,6/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 测试 LINQ 提供程序 添加更复杂的查询功能 现在已经添加了编译提供程序所需的所有片断。生成“LinqToTerraServerProvider”项目,并确认没有编译错误。 可通过创建包含 LINQ 查询的客户端应用程序,依据数据源来测试 LINQ 提供程序。 创建客户端应用程序以测试提供程序 1. 向解决方案中添加一个新的“控制台应用程序”项目,并将其命名为 ClientApp。 2. 在该新项目中,添加对提供程序程序集的引用。 3. 将“app.config”文件从提供程序项目拖到客户端项目。(此文件是与 Web 服务通信所必需的。) 4. 将以下 using 语句(Visual Basic 中为 Imports 语句)添加到“Program.cs”(或 Visual Basic 中的“Module1.vb”)文件: 5. 在“Program.cs”(或 Visual Basic 中的“Module1.vb”)文件的 Main 方法中,插入以下代码: 此代码将创建您在提供程序中定义的 IQueryable(T) 类型的新实例,然后使用 LINQ 查询该对象。查询通过使用相等表达式指定要基于其获取数据的位置。由于数据源实现 IQueryable,因此编译器会将查询表达式语 法转换为对 Queryable 中定义的标准查询运算符的调用。在内部,这些标准查询运算符方法会生成一个表达式目录树,并调用您作为 IQueryProvider 实现的一部分所实现的 Execute 或 CreateQuery 方法。 6. 生成“ClientApp”。 7. 将此客户端应用程序设置为您的解决方案的“启动”项目。在“解决方案资源管理器”中,右击“ClientApp”项目,并选择“设为启动项目”。 8. 运行程序并查看结果。应大约有三个结果。 注意: 在 Visual Basic 中,可能必须单击“显示所有文件”按钮才能在“解决方案资源管理器”中看到“app.config”文件。 C# 复制代码 using System; using System.Linq; using LinqToTerraServerProvider; C# 复制代码 QueryableTerraServerData terraPlaces = new QueryableTerraServerData(); var query = from place in terraPlaces where place.Name == "Johannesburg" select place.PlaceType; foreach (PlaceType placeType in query) Console.WriteLine(placeType); 到目前为止,对于您所拥有的提供程序,客户端只能采用非常有限的方式在 LINQ 查询中指定位置信息。具体而言,提供程序只能从诸如 Place.Name == "Seattle" 或 Place.State == "Alaska"(在 Visual Basic 中为 Place.Name = "Seattle" 或 Place.State = "Alaska")之类的相等表达式中获取位置信息。 接下来的过程演示如何添加对采用其他方式指定位置信息的支持。当您添加了此代码后,提供程序将能够从诸如 place.Name.StartsWith("Seat") 等方法调用表达式中提取位置信息。 添加对包含 String.StartsWith 的谓词的支持 1. 在“LinqToTerraServerProvider”项目中,将 VisitMethodCall 方法添加到 LocationFinder 类定义。 2. 重新编译“LinqToTerraServerProvider”项目。 3. 若要测试提供程序的新功能,请打开“ClientApp”项目中的“Program.cs”(或 Visual Basic 中的“Module1.vb”)文件。将 Main 方法中的代码替换为以下代码: 4. 运行程序并查看结果。应大约有 29 个结果。 下面的过程演示如何向提供程序中添加功能,以使客户端查询能够通过使用两个其他方法(具体来说是 Enumerable.Contains 和 List(T).Contains)来指定位置信息。当您添加了此代码后,提供程序将能够从客户端查询的方 法调用表达式(比如 placeList.Contains(place.Name))中提取位置信息,其中 placeList 集合是客户端提供的具体列表。允许客户端使用 Contains 方法的优势在于:客户端只需将位置添加到 placeList 中即可指 定任意数量的位置。改变位置的数量不会更改查询的语法。 添加对“where”子句中有 Contains 方法的查询的支持 1. 在“LinqToTerraServerProvider”项目的 LocationFinder 类定义中,将 VisitMethodCall 方法替换为以下代码: 此方法将 Contains 所应用于的集合中的每个字符串添加到位置列表中,以便用该列表查询 Web 服务。Enumerable 和 List(T) 中都定义了名为 Contains 的方法。因此,VisitMethodCall 方法必须同时检查这两种 声明类型。Enumerable.Contains 被定义为扩展方法;因此它所应用于的集合实际上是方法的第一个参数。List.Contains 被定义为实例方法;因此它所应用于的集合是方法的接收对象。 2. 重新编译“LinqToTerraServerProvider”项目。 C# 复制代码 protected override Expression VisitMethodCall(MethodCallExpression m) { if (m.Method.DeclaringType == typeof(String) && m.Method.Name == "StartsWith") { if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "Name") || ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "State")) { locations.Add(ExpressionTreeHelpers.GetValueFromExpression(m.Arguments[0])); return m; } } return base.VisitMethodCall(m); } C# 复制代码 QueryableTerraServerData terraPlaces = new QueryableTerraServerData(); var query = from place in terraPlaces where place.Name.StartsWith("Lond") select new { place.Name, place.State }; foreach (var obj in query) Console.WriteLine(obj); C# 复制代码 protected override Expression VisitMethodCall(MethodCallExpression m) { if (m.Method.DeclaringType == typeof(String) && m.Method.Name == "StartsWith") { if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "Name") || ExpressionTreeHelpers.IsSpecificMemberExpression(m.Object, typeof(Place), "State")) { locations.Add(ExpressionTreeHelpers.GetValueFromExpression(m.Arguments[0])); return m; } } else if (m.Method.Name == "Contains") { Expression valuesExpression = null; if (m.Method.DeclaringType == typeof(Enumerable)) { if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[1], typeof(Place), "Name") || ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[1], typeof(Place), "State")) { valuesExpression = m.Arguments[0]; } } else if (m.Method.DeclaringType == typeof(List)) { if (ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[0], typeof(Place), "Name") || ExpressionTreeHelpers.IsSpecificMemberExpression(m.Arguments[0], typeof(Place), "State")) { valuesExpression = m.Object; } } if (valuesExpression == null || valuesExpression.NodeType != ExpressionType.Constant) throw new Exception("Could not find the location values."); ConstantExpression ce = (ConstantExpression)valuesExpression; IEnumerable placeStrings = (IEnumerable)ce.Value; // Add each string in the collection to the list of locations to obtain data about. foreach (string place in placeStrings) locations.Add(place); return m; } return base.VisitMethodCall(m); } 页码,7/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 后续步骤 请参见 3. 若要测试提供程序的新功能,请打开“ClientApp”项目中的“Program.cs”(或 Visual Basic 中的“Module1.vb”)文件。将 Main 方法中的代码替换为以下代码: 4. 运行程序并查看结果。应大约有 5 个结果。 C# 复制代码 QueryableTerraServerData terraPlaces = new QueryableTerraServerData(); string[] places = { "Johannesburg", "Yachats", "Seattle" }; var query = from place in terraPlaces where places.Contains(place.Name) orderby place.State select new { place.Name, place.State }; foreach (var obj in query) Console.WriteLine(obj); 此演练主题演示如何为 Web 服务的一个方法创建 LINQ 提供程序。如果要继续进行 LINQ 提供程序的其他开发,请考虑以下可能性: z 使 LINQ 提供程序能够处理在客户端查询中指定位置的其他方式。 z 检查 TerraServer-USA Web 服务公开的其他方法,并创建与其中一个方法交互的 LINQ 提供程序。 z 找到其他感兴趣的 Web 服务,并为其创建 LINQ 提供程序。 z 为除 Web 服务外的数据源创建 LINQ 提供程序。 概念 启用数据源以进行 LINQ 查询 如何:实现表达式目录树访问器 如何:修改表达式目录树 Windows Communication Foundation 服务 参考 IQueryable(T) IOrderedQueryable(T) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,8/8(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/bd207f9b-7a4b-433e-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to Objects 发送反馈意见 术语“LINQ to Objects”是指直接对任意 IEnumerable 或 IEnumerable(T) 集合使用 LINQ 查询,无需使用中间 LINQ 提供程序或 API,如 LINQ to SQL 或 LINQ to XML。可以使用 LINQ 来查询任何可枚举的集合,如 List(T)、Array 或 Dictionary(TKey, TValue)。该集合可以是用户定义的集合,也可以是 .NET Framework API 返回的集合。 从根本上说,LINQ to Objects 表示一种新的处理集合的方法。采用旧方法,您必须编写指定如何从集合检索数据的 复杂的 foreach 循环。而采用 LINQ 方法,您只需编写描述要检索的内容的声明性代码。 另外,与传统的 foreach 循环相比,LINQ 查询具有三大优势: 1. 它们更简明、更易读,尤其在筛选多个条件时。 2. 它们使用最少的应用程序代码提供强大的筛选、排序和分组功能。 3. 无需修改或只需做很小的修改即可将它们移植到其他数据源。 通常,您要对数据执行的操作越复杂,您体会到的使用 LINQ 代替传统迭代技术的好处就越多。 本节的目的是使用一些精选示例来演示 LINQ 方法,而不打算详尽说明。 本节内容 如何:使用 LINQ 查询 ArrayList 演示如何使用 Visual Basic 和 C# 查询 ArrayList。 LINQ 和字符串 说明 LINQ 如何用于查询和转换字符串和字符串集合,还包括演示这些原则的主题链接。 LINQ 和文件目录 说明 LINQ 如何用于与文件系统进行交互,还包括演示这些概念的主题链接。 LINQ 和反射 演示 LINQ 如何使用反射的示例的链接。 语言集成查询 (LINQ) 提供说明 LINQ 并提供执行查询的代码示例的主题的链接。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/73cafe73-37cf-46e7-b... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 和字符串 请参见 发送反馈意见 LINQ 可用于查询和转换字符串和字符串集合。它对文本文件中的半结构化数据尤其有用。LINQ 查询可与传统的字 符串函数和正则表达式结合使用。例如,可以使用 Split 或 Split 方法来创建字符串数组,然后可以使用 LINQ 来查 询或修改此数组。可以在 LINQ 查询的 where 子句中使用 IsMatch 方法。可以使用 LINQ 来查询或修改由正则表 达式返回的 MatchCollection 结果。 还可以使用本节中介绍的技术将半结构化文本数据转换为 XML。有关更多信息,请参见如何: 从 CSV 文件生成 XML。 本节中的示例分为两种: 查询文本块 查询文本格式的半结构化数据 请参见 可以通过使用 Split 方法或 Split 方法将文本块拆分成可查询的较小字符串数组,来查询、分析和修改文本 块。可以将源文本拆分成词语、句子、段落、页或任何其他条件,然后根据查询的需要,执行其他拆分。 如何:对某词在字符串中出现的次数进行计数 (LINQ) 演示如何使用 LINQ 对文本进行简单查询。 如何:查询包含一组指定单词的句子 (LINQ) 演示如何按任意边界拆分文本文件,以及如何对每个部分执行查询。 如何:查询字符串中的字符 (LINQ) 演示字符串是可查询类型。 如何:将 LINQ 查询与正则表达式合并在一起 演示如何在 LINQ 查询中使用正则表达式来对筛选的查询结果执行复杂模式匹配。 许多不同类型的文本文件都包含一系列行,通常具有类似的格式,例如制表符分隔或逗号分隔文件或固定长度 的行。在将此类文本文件读取到内存中后,可以使用 LINQ 来查询和/或修改行。LINQ 查询还简化了组合多个 源的数据的任务。 如何:查找两个列表之间的差集 (LINQ) 演示如何查找位于一个列表中但不在另一个列表中的所有字符串。 如何:按任意词或字段对文本数据进行排序或筛选 (LINQ) 演示如何根据任意词语或字段排序文本行。 如何:重新排列带分隔符的文件的字段 (LINQ) 演示如何重新排列 .csv 文件中的某一行中的字段。 如何:组合和比较字符串集合 (LINQ) 演示如何以各种方式组合字符串列表。 如何: 从多个源填充对象集合 演示如何将多个文本文件用作数据源来创建对象集合。 如何:联接不同文件的内容 (LINQ) 演示如何使用匹配键将两个列表中的字符串组合为单个字符串。 如何:使用组将一个文件拆分成多个文件 (LINQ) 演示如何将单个文件用作数据源来创建新文件。 如何:在 CSV 文本文件中计算列值 (LINQ) 演示如何对 .csv 文件中的文本数据执行数学运算。 概念 语言集成查询 (LINQ) 如何: 从 CSV 文件生成 XML 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/6c34169f-7a39-436a-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:对某词在字符串中出现的次数进行计数 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何使用 LINQ 查询来计数指定词在字符串中的出现次数。请注意,若要执行计数,请先调用 Split 方法来创建词数组。Split 方法存在性能开销。如果 对字符串执行的唯一操作是计数词,则您应考虑改用 Matches 或 IndexOf 方法。但是,如果性能不是关键问题,或者您已拆分句子以对其执行其他类型的查询, 则使用 LINQ 来计数词或短语同样有意义。 示例 编译代码 请参见 C# 复制代码 class CountWords { static void Main() { string text = @"Historically, the world of data and the world of objects" + @" have not been well integrated. Programmers work in C# or Visual Basic" + @" and also in SQL or XQuery. On the one side are concepts such as classes," + @" objects, fields, inheritance, and .NET Framework APIs. On the other side" + @" are tables, columns, rows, nodes, and separate languages for dealing with" + @" them. Data types often require translation between the two worlds; there are" + @" different standard functions. Because the object world has no notion of query, a" + @" query can only be represented as a string without compile-time type checking or" + @" IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to" + @" objects in memory is often tedious and error-prone."; string searchTerm = "data"; //Convert the string into an array of words string[] source = text.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries); // Create and execute the query. It executes immediately // because a singleton value is produced. // Use ToLowerInvariant to match "data" and "Data" var matchQuery = from word in source where word.ToLowerInvariant() == searchTerm.ToLowerInvariant() select word; // Count the matches. int wordCount = matchQuery.Count(); Console.WriteLine("{0} occurrences(s) of the search term \"{1}\" were found.", wordCount, searchTerm); // Keep console window open in debug mode Console.WriteLine("Press any key to exit"); Console.ReadKey(); } } /* Output: 3 occurrences(s) of the search term "data" were found. */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/eef60723-c347-4fe9-a... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查询包含一组指定单词的句子 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何查找文本文件中包含指定的一组单词中每个单词的匹配项的句子。虽然在此示例中搜索条件数组是硬编码的,但也可 以在运行时动态填充此数组。在此示例中,查询返回包含单词“Historically”、“data”和“integrated”的句子。 示例 编译代码 请参见 查询运行时首先将文本拆分成句子,然后将句子拆分成包含每个单词的字符串数组。对于每个这样的数组,Distinct 方法移除所 有重复的单词,然后查询对单词数组和 wordstoMatch 数组执行 Intersect 操作。如果交集的计数与 wordsToMatch 数组的计 数相同,则在单词中找到了所有的单词,且返回原始句子。 在对 Split 的调用中,使用标点符号作为分隔符,以从字符串中移除标点符号。如果您没有这样做,则假如您有一个字符串 “Historically,”,该字符串不会与 wordsToMatch 数组中的“Historically”相匹配。根据源文本中标点的类型,您可能必须使用 其他分隔符。 C# 复制代码 class FindSentences { static void Main() { string text = @"Historically, the world of data and the world of objects " + @"have not been well integrated. Programmers work in C# or Visual Basic " + @"and also in SQL or XQuery. On the one side are concepts such as classes, " + @"objects, fields, inheritance, and .NET Framework APIs. On the other side " + @"are tables, columns, rows, nodes, and separate languages for dealing with " + @"them. Data types often require translation between the two worlds; there are " + @"different standard functions. Because the object world has no notion of query, a " + @"query can only be represented as a string without compile-time type checking or " + @"IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to " + @"objects in memory is often tedious and error-prone."; // Split the text block into an array of sentences. string[] sentences = text.Split(new char[] { '.', '?', '!' }); // Define the search terms. This list could also be dynamically populated at runtime. string[] wordsToMatch = { "Historically", "data", "integrated" }; // Find sentences that contain all the terms in the wordsToMatch array. // Note that the number of terms to match is not specified at compile time. var sentenceQuery = from sentence in sentences let w = sentence.Split(new char[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries) where w.Distinct().Intersect(wordsToMatch).Count() == wordsToMatch.Count() select sentence; // Execute the query. Note that you can explicitly type // the iteration variable here even though sentenceQuery // was implicitly typed. foreach (string str in sentenceQuery) { Console.WriteLine(str); } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } } /* Output: Historically, the world of data and the world of objects have not been well integrated */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/d94a3be1-986f-4460-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查询字符串中的字符 (LINQ) 示例 请参见 发送反馈意见 因为 String 类实现泛型 IEnumerable(T) 接口,所以可以将任何字符串作为字符序列进行查询。但是,这不是 LINQ 的常见用法。若要执行复杂的模式匹配操作,请使用 Regex 类。 示例 编译代码 请参见 下面的示例查询一个字符串以确定它包含的数字的数目。请注意,在首次执行此查询后将“重新使用”此查 询。这之所以可行是因为查询本身不存储任何实际结果。 C# 复制代码 class QueryAString { static void Main() { string aString = "ABCDE99F-J74-12-89A"; // Select only those characters that are numbers IEnumerable stringQuery = from ch in aString where Char.IsDigit(ch) select ch; // Execute the query foreach (char c in stringQuery) Console.Write(c + " "); // Call the Count method on the existing query. int count = stringQuery.Count(); Console.WriteLine("Count = {0}", count); // Select all characters before the first '-' IEnumerable stringQuery2 = aString.TakeWhile(c => c != '-'); // Execute the second query foreach (char c in stringQuery2) Console.Write(c); Console.WriteLine(System.Environment.NewLine + "Press any key to exit"); Console.ReadKey(); } } /* Output: Output: 9 9 7 4 1 2 8 9 Count = 8 ABCDE99F */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 如何:将 LINQ 查询与正则表达式合并在一起 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/d779fb0a-1fd3-4fb0-8... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:将 LINQ 查询与正则表达式合并在一起 示例 请参见 发送反馈意见 此示例演示如何使用 Regex 类创建正则表达式以便在文本字符串中进行更复杂的匹配。使用 LINQ 查询可以方便地对您要用正则表 达式搜索的文件进行准确筛选,以及对结果进行加工。 示例 请注意,您还可以查询由 RegEx 搜索返回的 MatchCollection 对象。在此示例中,结果中仅生成每个匹配项的值。但也可使用 C# 复制代码 class QueryWithRegEx { public static void Main() { // Modify this path as necessary. string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\"; // Take a snapshot of the file system. IEnumerable fileList = GetFiles(startFolder); // Create the regular expression to find all things "Visual". System.Text.RegularExpressions.Regex searchTerm = new System.Text.RegularExpressions.Regex(@"Visual (Basic|C#|C\+\+|J#|SourceSafe|Studio)"); // Search the contents of each .htm file. // Remove the where clause to find even more matches! // This query produces a list of files where a match // was found, and a list of the matches in that file. // Note: Explicit typing of "Match" in select clause. // This is required because MatchCollection is not a // generic IEnumerable collection. var queryMatchingFiles = from file in fileList where file.Extension == ".htm" let fileText = System.IO.File.ReadAllText(file.FullName) let matches = searchTerm.Matches(fileText) where searchTerm.Matches(fileText).Count > 0 select new { name = file.FullName, matches = from System.Text.RegularExpressions.Match match in matches select match.Value }; // Execute the query. Console.WriteLine("The term \"{0}\" was found in:", searchTerm.ToString()); foreach (var v in queryMatchingFiles) { // Trim the path a bit, then write // the file name in which a match was found. string s = v.name.Substring(startFolder.Length - 1); Console.WriteLine(s); // For this file, write out all the matching strings foreach (var v2 in v.matches) { Console.WriteLine(" " + v2); } } // Keep the console window open in debug mode Console.WriteLine("Press any key to exit"); Console.ReadKey(); } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { files.Add(new System.IO.FileInfo(name)); } return files; } } 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/4adbd3db-357f-4c78-... 编译代码 请参见 LINQ 对该集合执行各种筛选、排序和分组操作。由于 MatchCollection 是非泛型 IEnumerable 集合,因此必须显式声明查询中 的范围变量的类型。 z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用和针对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空 间的 using 指令。 z 将这段代码复制到项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 LINQ 和文件目录 如何: 从 CSV 文件生成 XML 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/4adbd3db-357f-4c78-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查找两个列表之间的差集 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何使用 LINQ 对两个字符串列表进行比较,并输出那些位于 names1.txt 中但不在 names2.txt 中的 行。 创建数据文件 z 按照如何:组合和比较字符串集合 (LINQ) 中的说明,将 names1.txt 和 names2.txt 复制到解决方案文件夹 中。 示例 编译代码 请参见 C# 和 Visual Basic 中的某些类型的查询操作(如 Except、Distinct、Union 和 Concat(TSource))只能用基于 方法的语法表示。 C# 复制代码 class CompareLists { static void Main() { // Create the IEnumerable data sources. string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt"); string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt"); // Create the query. Note that method syntax must be used here. IEnumerable differenceQuery = names1.Except(names2); // Execute the query. Console.WriteLine("The following lines are in names1.txt but not names2.txt"); foreach (string s in differenceQuery) Console.WriteLine(s); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } } /* Output: The following lines are in names1.txt but not names2.txt Potra, Cristina Noriega, Fabricio Aw, Kam Foo Toyoshima, Tim Guy, Wey Yuan Garcia, Debra */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用和针对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空间的 using 指令。 z 将这段代码复制到项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/14e185f0-8fce-46af-8... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:按任意词或字段对文本数据进行排序或筛选 (LINQ) 示例 请参见 发送反馈意见 下面的示例演示如何按结构化文本(如逗号分隔值)行中的任意字段对该文本行进行排序。可在运行时动态指定该 字段。假定 scores.csv 中的字段表示学生的 ID 号,后面跟四个测验分数。 创建一个包含数据的文件 z 从主题如何:联接不同文件的内容 (LINQ) 中复制 scores.csv 数据并将其保存到解决方案文件夹中。 示例 编译代码 此示例还演示如何从函数 (Visual Basic) 或方法 (C#) 返回查询变量。 C# 复制代码 public class SortLines { static void Main() { // Create an IEnumerable data source string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv"); // Change this to any value from 0 to 4. int sortField = 1; Console.WriteLine("Sorted highest to lowest by field [{0}]:", sortField); // Demonstrates how to return query from a method. // The query is executed here. foreach (string str in RunQuery(scores, sortField)) { Console.WriteLine(str); } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } // Returns the query variable, not query results! static IEnumerable RunQuery(IEnumerable source, int num) { // Split the string and sort on field[num] var scoreQuery = from line in source let fields = line.Split(',') orderby fields[num] descending select line; return scoreQuery; } } /* Output (if sortField == 1): Sorted highest to lowest by field [1]: 116, 99, 86, 90, 94 120, 99, 82, 81, 79 111, 97, 92, 81, 60 114, 97, 89, 85, 82 121, 96, 85, 91, 60 122, 94, 92, 91, 91 117, 93, 92, 80, 87 118, 92, 90, 83, 78 113, 88, 94, 65, 91 112, 75, 84, 91, 39 119, 68, 79, 88, 92 115, 35, 72, 91, 70 */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用和针对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空间的 using 指令。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2cad84a7-9a7b-40bc-... 请参见 z 将这段代码复制到项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2cad84a7-9a7b-40bc-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:重新排列带分隔符的文件的字段 (LINQ) 示例 请参见 发送反馈意见 逗号分隔值 (CSV) 文件是一种文本文件,通常用于存储电子表格数据或其他由行和列表示的表格数据。通过使用 Split 方法分隔字段,可以非常轻松地使用 LINQ 来查询和操作 CSV 文件。事实上,可以使用此技术来重新排列任何 结构化文本行部分;此技术不局限于 CSV 文件。 在下面的示例中,假定有三列分别代表学生的“姓氏”、“名字”和“ID”。这些字段基于学生的姓氏按字母顺序排 列。查询生成一个新序列,其中首先出现的是 ID 列,后面的第二列组合了学生的名字和姓氏。根据 ID 字段重新排 列各行。结果保存到新文件,但不修改原始数据。 创建数据文件 z 创建一个新的 Visual C# 或 Visual Basic 项目,然后将下面各行复制到名为 spreadsheet1.csv 的纯文本文件。 将此文件保存到您的解决方案文件夹。 示例 编译代码 复制代码 Adams,Terry,120 Fakhouri,Fadi,116 Feng,Hanying,117 Garcia,Cesar,114 Garcia,Debra,115 Garcia,Hugo,118 Mortensen,Sven,113 O'Donnell,Claire,112 Omelchenko,Svetlana,111 Tucker,Lance,119 Tucker,Michael,122 Zabokritski,Eugene,121 C# 复制代码 class CSVFiles { static void Main(string[] args) { // Create the IEnumerable data source string[] lines = System.IO.File.ReadAllLines(@"../../../spreadsheet1.csv"); // Create the query. Put field 2 first, then // reverse and combine fields 0 and 1 from the old field IEnumerable query = from line in lines let x = line.Split(',') orderby x[2] select x[2] + ", " + (x[1] + " " + x[0]); // Execute the query and write out the new file. Note that WriteAllLines // takes a string[], so ToArray is called on the query. System.IO.File.WriteAllLines(@"../../../spreadsheet2.csv", query.ToArray()); Console.WriteLine("Spreadsheet2.csv written to disk. Press any key to exit"); Console.ReadKey(); } } /* Output to spreadsheet2.csv: 111, Svetlana Omelchenko 112, Claire O'Donnell 113, Sven Mortensen 114, Cesar Garcia 115, Debra Garcia 116, Fadi Fakhouri 117, Hanying Feng 118, Hugo Garcia 119, Lance Tucker 120, Terry Adams 121, Eugene Zabokritski 122, Michael Tucker */ 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/96f92f1e-1536-46ac-8... 请参见 z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 LINQ 和文件目录 如何: 从 CSV 文件生成 XML 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/96f92f1e-1536-46ac-8... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:组合和比较字符串集合 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何合并包含文本行的文件,然后排序结果。具体来说,此示例演示如何对两组文本行执行简单的串联、联合和交集。 设置项目和文本文件 1. 将下面的姓名复制到名为 names1.txt 的文本文件,然后将此文件保存到您的解决方案文件夹: 2. 将下面的姓名复制到名为 names2.txt 的文本文件,然后将此文件保存到您的解决方案文件夹。请注意,这两个文件有一些共同的姓名。 示例 复制代码 Bankov, Peter Holm, Michael Garcia, Hugo Potra, Cristina Noriega, Fabricio Aw, Kam Foo Beebe, Ann Toyoshima, Tim Guy, Wey Yuan Garcia, Debra 复制代码 Liu, Jinghao Bankov, Peter Holm, Michael Garcia, Hugo Beebe, Ann Gilchrist, Beth Myrcha, Jacek Giakoumakis, Leo McLin, Nkenge El Yassir, Mehdi C# 复制代码 class MergeStrings { static void Main(string[] args) { //Put text files in your solution folder string[] fileA = System.IO.File.ReadAllLines(@"../../../names1.txt"); string[] fileB = System.IO.File.ReadAllLines(@"../../../names2.txt"); //Simple concatenation and sort. Duplicates are preserved. IEnumerable concatQuery = fileA.Concat(fileB).OrderBy(s => s); // Pass the query variable to another function for execution. OutputQueryResults(concatQuery, "Simple concatenate and sort. Duplicates are preserved:"); // Concatenate and remove duplicate names based on // default string comparer. IEnumerable uniqueNamesQuery = fileA.Union(fileB).OrderBy(s => s); OutputQueryResults(uniqueNamesQuery, "Union removes duplicate names:"); // Find the names that occur in both files (based on // default string comparer). IEnumerable commonNamesQuery = fileA.Intersect(fileB); OutputQueryResults(commonNamesQuery, "Merge based on intersect:"); // Find the matching fields in each list. Merge the two // results by using Concat, and then // sort using the default string comparer. string nameMatch = "Garcia"; IEnumerable tempQuery1 = from name in fileA let n = name.Split(',') where n[0] == nameMatch select name; IEnumerable tempQuery2 = from name2 in fileB let n2 = name2.Split(',') where n2[0] == nameMatch select name2; IEnumerable nameMatchQuery = tempQuery1.Concat(tempQuery2).OrderBy(s => s); OutputQueryResults(nameMatchQuery, String.Format("Concat based on partial name match \"{0}\":", nameMatch)); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } static void OutputQueryResults(IEnumerable query, string message) { Console.WriteLine(System.Environment.NewLine + message); foreach (string item in query) { Console.WriteLine(item); } Console.WriteLine("{0} total names in list", query.Count()); } 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/71c890f4-2e48-4cc6-... 编译代码 请参见 } /* Output: Simple concatenate and sort. Duplicates are preserved: Aw, Kam Foo Bankov, Peter Bankov, Peter Beebe, Ann Beebe, Ann El Yassir, Mehdi Garcia, Debra Garcia, Hugo Garcia, Hugo Giakoumakis, Leo Gilchrist, Beth Guy, Wey Yuan Holm, Michael Holm, Michael Liu, Jinghao McLin, Nkenge Myrcha, Jacek Noriega, Fabricio Potra, Cristina Toyoshima, Tim 20 total names in list Union removes duplicate names: Aw, Kam Foo Bankov, Peter Beebe, Ann El Yassir, Mehdi Garcia, Debra Garcia, Hugo Giakoumakis, Leo Gilchrist, Beth Guy, Wey Yuan Holm, Michael Liu, Jinghao McLin, Nkenge Myrcha, Jacek Noriega, Fabricio Potra, Cristina Toyoshima, Tim 16 total names in list Merge based on intersect: Bankov, Peter Holm, Michael Garcia, Hugo Beebe, Ann 4 total names in list Concat based on partial name match "Garcia": Garcia, Debra Garcia, Hugo Garcia, Hugo 3 total names in list */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/71c890f4-2e48-4cc6-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:从多个源填充对象集合 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何将不同源类型的数据合并为新类型的序列。以下代码中的示例可合并字符串和整数数组。但是,相同的原 则也适用于任意两个数据源,包括内存中对象(包括 LINQ to SQL 查询结果、ADO.NET 数据集和 XML 文档)的任意组 合。 创建数据文件 z 创建一个新的 Visual C# 或 Visual Basic 项目,然后将下面的 names.csv 和 scores.csv 文件复制到解决方案文件夹 中,如如何:联接不同文件的内容 (LINQ) 中所述。 示例 注意: 不要尝试将内存中的数据或文件系统中的数据与仍在数据库中的数据相联接。此种跨域联接会生成未定义的结果,因 为为数据库查询和其他类型的源定义联接运算的方式可能不同。另外,如果数据库中的数据量足够大,则存在此类运 算引发内存不足异常的风险。若要将数据库数据与内存中的数据相联接,请首先对数据库查询调用 ToList 或 ToArray,然后对返回的集合执行联接。 下面的示例演示如何使用命名类型 Student 来存储两个模拟 .csv 格式的电子表格数据的内存中的字符串集合的合 并数据。第一个字符串集合表示学生姓名和 ID,第二个集合表示学生 ID(在第一列中)和四次考试分数。 C# 复制代码 class Student { public string FirstName { get; set; } public string LastName { get; set; } public int ID { get; set; } public List ExamScores { get; set; } } class PopulateCollections { static void Main() { // These data files are defined in How to: Join Content from Dissimilar Files (LINQ) string[] names = System.IO.File.ReadAllLines(@"../../../names.csv"); string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv"); // Merge the data sources using a named type. // var could be used instead of an explicit type. // Note the dynamic creation of a list of ints for the // TestScores member. We skip 1 because the first string // in the array is the student ID, not an exam score. IEnumerable queryNamesScores = from name in names let x = name.Split(',') from score in scores let s = score.Split(',') where x[2] == s[0] select new Student() { FirstName = x[0], LastName = x[1], ID = Convert.ToInt32(x[2]), ExamScores = (from scoreAsText in s.Skip(1) select Convert.ToInt32(scoreAsText)). ToList() }; // Optional. Store the newly created student objects in memory // for faster access in future queries. Could be useful with // very large data files. List students = queryNamesScores.ToList(); // Display the results and perform one further calculation. foreach (var student in students) { Console.WriteLine("The average score of {0} {1} is {2}.", student.FirstName, student.LastName, student.ExamScores.Average()); } //Keep console window open in debug mode Console.WriteLine("Press any key to exit."); Console.ReadKey(); } 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a9972a9c-e7c1-41d4-... 编译代码 请参见 这些示例中的数据源是使用对象初始值设定项初始化的。查询使用 join 子句来将姓名与分数相匹配。将 ID 用作外 键。但是,在一个源中,ID 是字符串,而在另一个源中,ID 是整数。因为 join 需要进行相等比较,所以您必须首 先从字符串中提取 ID,然后将它转换为整数。这在两个 let 子句中实现。第一个 let 子句中的临时标识符 x 存储 通过在每个空格处拆分原始字符串而创建的三个字符串的数组。第二个 let 子句中的标识符 n 存储将 ID 子字符串 转换为整数的结果。在 select 子句中,对象初始值设定项用于使用两个源的数据来实例化每个新 Student 对象。 如果您不需要存储查询结果,则匿名类型会比命名类型更方便。如果您要将查询结果传递到执行此查询所用的方法 外部,则需要使用命名类型。 } /* Output: The average score of Adams Terry is 85.25. The average score of Fakhouri Fadi is 92.25. The average score of Feng Hanying is 88. The average score of Garcia Cesar is 88.25. The average score of Garcia Debra is 67. The average score of Garcia Hugo is 85.75. The average score of Mortensen Sven is 84.5. The average score of O'Donnell Claire is 72.25. The average score of Omelchenko Svetlana is 82.5. The average score of Tucker Lance is 81.75. The average score of Tucker Michael is 92. The average score of Zabokritski Eugene is 83. */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的 引用以及针对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 对象和集合初始值设定项(C# 编程指南) 匿名类型(C# 编程指南) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/a9972a9c-e7c1-41d4-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:使用组将一个文件拆分成多个文件 (LINQ) 示例 请参见 发送反馈意见 此示例演示一种进行以下操作的方法:合并两个文件的内容,然后创建一组以新方式组织数据的新文件。 创建数据文件 1. 将下面的姓名复制到名为 names1.txt 的文本文件,然后将此文件保存到您的解决方案文件夹: 2. 将下面的姓名复制到名为 names2.txt 的文本文件,然后将此文件保存到您的解决方案文件夹。注意这两个文 件有一些共同的姓名。 示例 复制代码 Bankov, Peter Holm, Michael Garcia, Hugo Potra, Cristina Noriega, Fabricio Aw, Kam Foo Beebe, Ann Toyoshima, Tim Guy, Wey Yuan Garcia, Debra 复制代码 Liu, Jinghao Bankov, Peter Holm, Michael Garcia, Hugo Beebe, Ann Gilchrist, Beth Myrcha, Jacek Giakoumakis, Leo McLin, Nkenge El Yassir, Mehdi C# 复制代码 class SplitWithGroups { static void Main() { string[] fileA = System.IO.File.ReadAllLines(@"../../../names1.txt"); string[] fileB = System.IO.File.ReadAllLines(@"../../../names2.txt"); // Concatenate and remove duplicate names based on // default string comparer var mergeQuery = fileA.Union(fileB); // Group the names by the first letter in the last name. var groupQuery = from name in mergeQuery let n = name.Split(',') group name by n[0][0] into g orderby g.Key select g; // Create a new file for each group that was created // Note that nested foreach loops are required to access // individual items with each group. foreach (var g in groupQuery) { // Create the new file name. string fileName = @"../../../testFile_" + g.Key + ".txt"; // Output to display. Console.WriteLine(g.Key); // Write file. using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fileName)) { foreach (var item in g) { sw.WriteLine(item); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/681a4ed9-d433-41ba-... 编译代码 请参见 对于与数据文件位于同一文件夹中的每个组,程序将为这些组编写单独的文件。 // Output to console for example purposes. Console.WriteLine(" {0}", item); } } } // Keep console window open in debug mode. Console.WriteLine("Files have been written. Press any key to exit"); Console.ReadKey(); } } /* Output: A Aw, Kam Foo B Bankov, Peter Beebe, Ann E El Yassir, Mehdi G Garcia, Hugo Guy, Wey Yuan Garcia, Debra Gilchrist, Beth Giakoumakis, Leo H Holm, Michael L Liu, Jinghao M Myrcha, Jacek McLin, Nkenge N Noriega, Fabricio P Potra, Cristina T Toyoshima, Tim */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/681a4ed9-d433-41ba-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:联接不同文件的内容 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何联接两个共享一个用作匹配键的共同值的逗号分隔文件的数据。如果您必须将两个电子表格的数据 或一个电子表格和一个其他格式的文件的数据组合为一个新文件,则此技术很有用。还可以修改此示例以适合任意 种类的结构化文本。 创建数据文件 1. 将下面这些行复制到名为 scores.csv 的文件中并将此文件保存到您的项目文件所在的文件夹。此文件表示电 子表格数据。第 1 列是学生的 ID,第 2 至 5 列是测验分数。 2. 将下面这些行复制到名为 names.csv 的文件中并将此文件保存到您的项目文件所在的文件夹。此文件表示一 个电子表格,该电子表格包含学生的姓氏、名字和学生 ID。 示例 复制代码 111, 97, 92, 81, 60 112, 75, 84, 91, 39 113, 88, 94, 65, 91 114, 97, 89, 85, 82 115, 35, 72, 91, 70 116, 99, 86, 90, 94 117, 93, 92, 80, 87 118, 92, 90, 83, 78 119, 68, 79, 88, 92 120, 99, 82, 81, 79 121, 96, 85, 91, 60 122, 94, 92, 91, 91 复制代码 Omelchenko,Svetlana,111 O'Donnell,Claire,112 Mortensen,Sven,113 Garcia,Cesar,114 Garcia,Debra,115 Fakhouri,Fadi,116 Feng,Hanying,117 Garcia,Hugo,118 Tucker,Lance,119 Adams,Terry,120 Zabokritski,Eugene,121 Tucker,Michael,122 C# 复制代码 class JoinStrings { static void Main() { // Join content from dissimilar files that contain // related information. names.csv contains the student name // plus an ID number. scores. csv contains the ID and a // set of four test scores. The following query joins // the scores to the student names by using ID as a // matching key. string[] names = System.IO.File.ReadAllLines(@"../../../names.csv"); string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv"); // Name: Last[0], First[1], ID[2], Grade Level[3] // Omelchenko, Svetlana, 11, 2 // Score: StudentID[0], Exam1[1] Exam2[2], Exam3[3], Exam4[4] // 111, 97, 92, 81, 60 // This query joins two dissimilar spreadsheets based on common ID value. // Multiple from clauses are used instead of a join clause // in order to store results of id.Split. IEnumerable scoreQuery1 = from name in names let nameFields = name.Split(',') from id in scores let scoreFields = id.Split(',') 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/9ce5ce84-b38f-4e48-b... 编译代码 请参见 where nameFields[2] == scoreFields[0] select nameFields[0] + "," + scoreFields[1] + "," + scoreFields[2] + "," + scoreFields[3] + "," + scoreFields[4]; // Pass a query variable to a method and // execute it in the method. The query itself // is unchanged. OutputQueryResults(scoreQuery1, "Merge two spreadsheets:"); // Keep console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } static void OutputQueryResults(IEnumerable query, string message) { Console.WriteLine(System.Environment.NewLine + message); foreach (string item in query) { Console.WriteLine(item); } Console.WriteLine("{0} total names in list", query.Count()); } } /* Output: Merge two spreadsheets: Adams, 99, 82, 81, 79 Fakhouri, 99, 86, 90, 94 Feng, 93, 92, 80, 87 Garcia, 97, 89, 85, 82 Garcia, 35, 72, 91, 70 Garcia, 92, 90, 83, 78 Mortensen, 88, 94, 65, 91 O'Donnell, 75, 84, 91, 39 Omelchenko, 97, 92, 81, 60 Tucker, 68, 79, 88, 92 Tucker, 94, 92, 91, 91 Zabokritski, 96, 85, 91, 60 12 total names in list */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/9ce5ce84-b38f-4e48-b... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:在 CSV 文本文件中计算列值 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何对 .csv 文件的列执行诸如 Sum、Average、Min 和 Max 等聚合计算。此处所示的示例原则可以应用于其 他类型的结构化文本。 创建源文件 z 将下面这些行复制到名为 scores.csv 的文件中,并将此文件保存到您的解决方案文件夹。假定第一列表示学员 ID, 后面几列表示四次考试的分数。 示例 复制代码 111, 97, 92, 81, 60 112, 75, 84, 91, 39 113, 88, 94, 65, 91 114, 97, 89, 85, 82 115, 35, 72, 91, 70 116, 99, 86, 90, 94 117, 93, 92, 80, 87 118, 92, 90, 83, 78 119, 68, 79, 88, 92 120, 99, 82, 81, 79 121, 96, 85, 91, 60 122, 94, 92, 91, 91 C# 复制代码 class SumColumns { static void Main(string[] args) { string[] lines = System.IO.File.ReadAllLines(@"../../../scores.csv"); // Specifies the column to compute int exam = 3; // Spreadsheet format: // Student ID Exam#1 Exam#2 Exam#3 Exam#4 // 111, 97, 92, 81, 60 // one is added to skip over the first column // which holds the student ID. SingleColumn(lines, exam + 1); Console.WriteLine(); MultiColumns(lines); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } static void SingleColumn(IEnumerable strs, int examNum) { Console.WriteLine("Single Column Query:"); // examNum specifies the column to run the // calculations on. This could also be // passed in dynamically at runtime. // columnQuery is a IEnumerable // This query performs two steps: // 1) split the string into a string[] // 2) convert the specified element to // int and select it. var columnQuery = from line in strs let x = line.Split(',') select Convert.ToInt32(x[examNum]); // Execute and cache the results for performance. // Only needed with very large files. var results = columnQuery.ToList(); // Perform aggregate calculations // on the column specified by examNum. double average = results.Average(); int max = results.Max(); int min = results.Min(); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/97218eb1-d035-4f8d-... 编译代码 请参见 如果文件是制表符分隔文件,只需将 Split 方法中的参数更新为 \t。 Console.WriteLine("Exam #{0}: Average:{1:##.##} High Score:{2} Low Score:{3}", examNum, average, max, min); } static void MultiColumns(IEnumerable strs) { Console.WriteLine("Multi Column Query:"); // Create the columnQuery. Explicit typing is used // to make clear that the columnQuery will produce // nested sequences. You can also just use 'var'. // The columnQuery performs these steps: // 1) convert the string to a string[] // 2) skip over the "Student ID" column and take the rest // 3) convert each string to an int and select that // entire sequence as one row in the results. IEnumerable> query = from line in strs let x = line.Split(',') let y = x.Skip(1) select (from str in y select Convert.ToInt32(str)); // Execute and cache the results for performance. // ToArray could also be used here. var results = query.ToList(); // Find out how many columns we have. int columnCount = results[0].Count(); // Perform aggregate calculations on each column. // One loop for each score column in scores. // We can use a for loop because we have already // executed the columnQuery in the call to ToList. for (int column = 0; column < columnCount; column++) { var res2 = from row in results select row.ElementAt(column); double average = res2.Average(); int max = res2.Max(); int min = res2.Min(); // 1 is added to column because Exam numbers // begin with 1 Console.WriteLine("Exam #{0} Average: {1:##.##} High Score: {2} Low Score: {3}", column + 1, average, max, min); } } } /* Output: Single Column Query: Exam #4: Average:76.92 High Score:94 Low Score:39 Multi Column Query: Exam #1 Average: 86.08 High Score: 99 Low Score: 35 Exam #2 Average: 86.42 High Score: 94 Low Score: 72 Exam #3 Average: 84.75 High Score: 91 Low Score: 65 Exam #4 Average: 76.92 High Score: 94 Low Score: 39 */ z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的 引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。 z 将此代码复制到您的项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ 和字符串 LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/97218eb1-d035-4f8d-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 和反射 请参见 发送反馈意见 .NET Framework 类库反射 API 可用于检查 .NET 程序集中的元数据,及创建位于该程序集中的类型、类型成员、参 数等等的集合。因为这些集合支持泛型 IEnumerable 接口,所以可以使用 LINQ 查询它们。 有关更多信息,请参见Reflector 示例示例应用程序。 本节包含下列主题: 如何:使用反射查询程序集的元数据 (LINQ) 演示如何将 LINQ 与反射一起使用。 请参见 概念 LINQ to Objects Reflector 示例 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/b095b35c-0753-437d-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:使用反射查询程序集的元数据 (LINQ) 示例 请参见 发送反馈意见 下面的示例演示 LINQ 如何与反射一起用于检索有关与指定的搜索条件相匹配的方法的特定元数据。在本例中,查询将查找程序集中返回可枚举的类型(如数组)的所有方法的名称。 示例 编译代码 请参见 此示例使用 GetTypes 方法返回指定程序集中的类型的数组。应用 where 筛选器的目的是只返回公共类型。对于各个公共类型,将使用从 GetMethods 调用返回的 MethodInfo 数组生成子查询。这些结果经过筛选,只返回其返回类型为数组或实现 IEnumerable(T) 的其他 类型的那些方法。最后,将类型名称用作键对这些结果进行分组。 C# 复制代码 using System.Reflection; using System.IO; namespace LINQReflection { class ReflectionHowTO { static void Main(string[] args) { string file = @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll"; Assembly assembly = Assembly.LoadFrom(file); var pubTypesQuery = from type in assembly.GetTypes() where type.IsPublic from method in type.GetMethods() where method.ReturnType.IsArray == true || (method.ReturnType.GetInterface(typeof(System.Collections.Generic.IEnumerable<>).FullName) != null && method.ReturnType.FullName != "System.String") group method.ToString() by type.ToString(); foreach (var groupOfMethods in pubTypesQuery) { Console.WriteLine("Type: {0}", groupOfMethods.Key); foreach (var method in groupOfMethods) { Console.WriteLine(" {0}", method); } } Console.WriteLine("Press any key to exit"); Console.ReadKey(); } } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用,以及 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 概念 LINQ to Objects 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/7cee5334-e39b-40ff-b... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ 和文件目录 请参见 发送反馈意见 许多文件系统操作实质上是查询,因此非常适合使用 LINQ 方法。 请注意,本节中的查询是非破坏性查询。它们不用于更改原始文件或文件夹的内容。这遵循了查询不应引起任何副 作用这条规则。通常,修改源数据的任何代码(包括执行创建/更新/删除运算符的查询)应与只查询数据的代码分 开。 本节包含下列主题: 如何:查询具有指定属性或名称的文件 演示如何通过检查文件的 FileInfo 对象的一个或多个属性来搜索文件。 如何:按扩展名对文件进行分组 (LINQ) 演示如何根据文件扩展名返回 FileInfo 对象组。 如何:查询一组文件夹中的总字节数 (LINQ) 演示如何返回指定目录树中的所有文件中的总字节数。 如何:比较两个文件夹的内容 (LINQ) 演示如何返回位于两个指定文件夹中的所有文件,以及仅位于其中一个文件夹中的所有文件。 如何:查询目录树中的一个或多个最大的文件 (LINQ) 演示如何返回目录树中的最大文件、最小文件或指定数量的文件。 如何:在目录树中查询重复文件 (LINQ) 演示如何对出现在指定目录树的多个位置的所有文件名进行分组。此外,还演示如何根据自定义比较器执行更复杂 的比较。 如何:查询文件夹中的文件的内容 (LINQ) 演示如何循环访问树中的文件夹,打开每个文件以及查询文件的内容。 注释 请参见 注意: 如果要对多种类型的文件和文档的内容执行编程查询,可以考虑使用 Windows Desktop Search Engine (Windows 桌面搜索引擎)。虽然当前无法使用 LINQ 进行查询,但它提供了功能强大的索引服务,可有效地 管理文件系统的复杂性。 创建准确表示文件系统的内容并适当处理异常的数据源,存在一些难度。本节中的示例创建 FileInfo 对象的快 照集合,该集合表示指定的根文件夹及其所有子文件夹下的所有文件。每个 FileInfo 的实际状态可能会在您开 始和结束执行查询过程中发生更改。例如,您可以创建 FileInfo 对象的列表来用作数据源。如果您尝试通过查 询访问 Length 属性,则 FileInfo 对象会尝试访问文件系统来更新 Length 的值。如果该文件不再存在,则 您会在查询中获得 FileNotFoundException,即使您没有直接查询文件系统也是如此。本节中的一些查询使用 不同的方法,在某些情况下使用该方法不会出现这些特定异常。另一种方法是使用 FileSystemWatcher 保持数 据源动态更新。 概念 LINQ to Objects 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/5a5d516c-0279-4a84-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查询具有指定属性或名称的文件 示例 请参见 发送反馈意见 此示例演示如何查找指定目录树中具有指定文件扩展名(例如“.txt”)的所有文件,还演示如何根据创建时间返回树中最新或最旧的文件。 示例 编译代码 可靠编程 请参见 C# 复制代码 class FindFileByExtension { // This query will produce the full path for all .txt files // under the specified folder including subfolders. // It orders the list according to the file name. static void Main() { string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\"; // Take a snapshot of the file system. IEnumerable fileList = GetFiles(startFolder); //Create the query IEnumerable fileQuery = from file in fileList where file.Extension == ".txt" orderby file.Name select file; //Execute the query. This might write out a lot of files! foreach (System.IO.FileInfo fi in fileQuery) { Console.WriteLine(fi.FullName); } // Create and execute a new query by using the previous // query as a starting point. fileQuery is not // executed again until the call to Last() var newestFile = (from file in fileQuery orderby file.CreationTime select new { file.FullName, file.CreationTime }) .Last(); Console.WriteLine("\r\nThe newest .txt file is {0}. Creation time: {1}", newestFile.FullName, newestFile.CreationTime); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { files.Add(new System.IO.FileInfo(name)); } return files; } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用以及针对 System.Linq 命名空 间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行大量查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/be2c2602-4a38-46d9-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:按扩展名对文件进行分组 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何使用 LINQ 对文件或文件夹列表执行高级分组和排序操作。此外,它还演示如何使用 Skip(TSource) 和 Take(TSource) 方法对控制 台窗口中的输出进行分页。 示例 下面的查询演示如何按文件扩展名对指定目录树的内容进行分组。 C# 复制代码 class GroupByExtension { // This query will sort all the files under the specified folder // and subfolder into groups keyed by the file extension. private static void Main() { // Take a snapshot of the file system. string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\Common7"; // Used in WriteLine to trim output lines. int trimLength = startFolder.Length; // Take a snapshot of the file system. IEnumerable fileList = GetFiles(startFolder); // Create the query. var queryGroupByExt = from file in fileList group file by file.Extension.ToLower() into fileGroup orderby fileGroup.Key select fileGroup; // Display one group at a time. If the number of // entries is greater than the number of lines // in the console window, then page the output. PageOutput(trimLength, queryGroupByExt); } // This method specifically handles group queries of FileInfo objects with string keys. // It can be modified to work for any long listings of data. Note that explicit typing // must be used in method signatures. The groupbyExtList parameter is a query that produces // groups of FileInfo objects with string keys. private static void PageOutput( int rootLength, IEnumerable> groupByExtList) { // Flag to break out of paging loop. bool goAgain = true; // "3" = 1 line for extension + 1 for "Press any key" + 1 for input cursor. int numLines = Console.WindowHeight - 3; // Iterate through the outer collection of groups. foreach (var filegroup in groupByExtList) { // Start a new extension at the top of a page. int currentLine = 0; // Output only as many lines of the current group as will fit in the window. do { Console.Clear(); Console.WriteLine(filegroup.Key == String.Empty ? "[none]" : filegroup.Key); // Get 'numLines' number of items starting at number 'currentLine'. var resultPage = filegroup.Skip(currentLine).Take(numLines); //Execute the resultPage query foreach (var f in resultPage) { Console.WriteLine("\t{0}", f.FullName.Substring(rootLength)); } // Increment the line counter. currentLine += numLines; // Give the user a chance to escape. Console.WriteLine("Press any key to continue or the 'End' key to break..."); ConsoleKey key = Console.ReadKey().Key; if (key == ConsoleKey.End) { goAgain = false; break; } } while (currentLine < filegroup.Count()); if (goAgain == false) break; } } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/25169a8b-509c-4a21-... 编译代码 可靠编程 请参见 此程序的输出可能会很长,具体取决于本地文件系统的细节以及 startFolder 的设置。为了使您可以查看所有结果,此示例还演示如何按页 查看结果。这些方法可应用于 Windows 和 Web 应用程序。请注意,由于代码将对组中的项进行分页,因此需要嵌套的 foreach 循环。此 外,还会使用某他某个逻辑来计算列表中的当前位置,以及使用户可以停止分页并退出程序。在这种特定情况下,将针对原始查询的缓存结果 运行分页查询。在其他上下文(如 LINQ to SQL)中,不需要这种缓存。 throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { files.Add(new System.IO.FileInfo(name)); } return files; } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用和针对 System.Linq 命 名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空间的 using 指令。 z 将这段代码复制到项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行密集型查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/25169a8b-509c-4a21-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查询一组文件夹中的总字节数 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何检索指定文件夹及其所有子文件夹中的所有文件所使用的总字节数。 示例 编译代码 可靠编程 请参见 Sum 方法添加在 select 子句中选择的所有项的值。您可以轻松修改此查询以检索指定目录树中的最大或最小文件,方法是调用 Min 或 Max 方法,而不是 Sum。 如果您只需要统计特定目录树中的字节数,则可以更高效地实现此目的,而无需创建 LINQ 查询,因为该查询会引发创建列表集合作为数据源的系统开销。 随着查询复杂度的增加,或者当您必须对同一数据源运行多个查询时,LINQ 方法的有用性也会随之增加。 查询调用至单独的方法来获取文件长度。它这样做的目的是,抑制在以下情形下可能引发的异常:在对 GetFiles 的调用中创建 FileInfo 对象后在其他线程 上删除该文件。虽然已创建了 FileInfo 对象,但仍会发生异常,因为 FileInfo 对象将尝试使用第一次访问其 Length 属性时的最新长度刷新此属性。通过将此 操作放在查询外的 try-catch 块中,该代码遵循了避免在查询中执行可引发副作用的操作的规则。通常,在抑制异常时必须格外谨慎,确保没有将应用程序置 于未知状态。 C# 复制代码 class QuerySize { public static void Main() { string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\VC#"; // Take a snapshot of the file system. // This method assumes that the application has discovery permissions // for all folders under the specified path. IEnumerable fileList = System.IO.Directory.GetFiles(startFolder, "*.*", System.IO.SearchOption.AllDirectories); var fileQuery = from file in fileList select GetFileLength(file); // Cache the results to avoid multiple trips to the file system. long[] fileLengths = fileQuery.ToArray(); // Return the size of the largest file long largestFile = fileLengths.Max(); // Return the total number of bytes in all the files under the specified folder. long totalBytes = fileLengths.Sum(); Console.WriteLine("There are {0} bytes in {1} files under {2}", totalBytes, fileList.Count(), startFolder); Console.WriteLine("The largest files is {0} bytes.", largestFile); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } // This method is used to swallow the possible exception // that can be raised when accessing the System.IO.FileInfo.Length property. static long GetFileLength(string filename) { long retval; try { System.IO.FileInfo fi = new System.IO.FileInfo(filename); retval = fi.Length; } catch (System.IO.FileNotFoundException) { // If a file is no longer present, // just add zero bytes to the total. retval = 0; } return retval; } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用以及针对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行大量查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/f3ee6ef0-b3fc-464c-a... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:比较两个文件夹的内容 (LINQ) 示例 请参见 发送反馈意见 此示例演示比较两个文件列表的三种方法: z 查询一个指定两个文件列表是否相同的布尔值。 z 查询用于检索同时位于两个文件夹中的文件的交集。 z 查询用于检索位于一个文件夹中但不在另一个文件夹中的文件的差集。 此处显示的 FileComparer 类演示如何将自定义比较器类与标准查询运算符一起使用。该类不是为在实际方案中使用而设计的。它只是使用每个文件的 名称和长度(以字节为单位)来确定每个文件夹的内容是否相同。在实际方案中,应对此比较器进行修改以执行更严格的相等性检查。 示例 注意: 可以修改上述这些方法以便对任意类型的对象序列进行比较。 C# 复制代码 namespace QueryCompareTwoDirs { class CompareDirs { static void Main(string[] args) { // Create two identical or different temporary folders // on a local drive and change these file paths. string pathA = @"C:\TestDir"; string pathB = @"C:\TestDir2"; // Take a snapshot of the file system. IEnumerable list1 = GetFiles(pathA); IEnumerable list2 = GetFiles(pathB); //A custom file comparer defined below FileCompare myFileCompare = new FileCompare(); // This query determines whether the two folders contain // identical file lists, based on the custom file comparer // that is defined in the FileCompare class. // The query executes immediately because it returns a bool. bool areIdentical = list1.SequenceEqual(list2, myFileCompare); if (areIdentical == true) { Console.WriteLine("the two folders are the same"); } else { Console.WriteLine("The two folders are not the same"); } // Find the common files. It produces a sequence and doesn't // execute until the foreach statement. var queryCommonFiles = list1.Intersect(list2, myFileCompare); if (queryCommonFiles.Count() > 0) { Console.WriteLine("The following files are in both folders:"); foreach (var v in queryCommonFiles) { Console.WriteLine(v.FullName); //shows which items end up in result list } } else { Console.WriteLine("There are no common files in the two folders."); } // Find the set difference between the two folders. // For this example we only check one way. var queryList1Only = (from file in list1 select file).Except(list2, myFileCompare); Console.WriteLine("The following files are in list1 but not list2:"); foreach (var v in queryList1Only) { Console.WriteLine(v.FullName); } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2d0ee383-7c6c-49ed-... 编译代码 可靠编程 请参见 files.Add(new System.IO.FileInfo(name)); } return files; } } // This implementation defines a very simple comparison // between two FileInfo objects. It only compares the name // of the files being compared and their length in bytes. class FileCompare : System.Collections.Generic.IEqualityComparer { public FileCompare() { } public bool Equals(System.IO.FileInfo f1, System.IO.FileInfo f2) { return (f1.Name == f2.Name && f1.Length == f2.Length); } // Return a hash that reflects the comparison criteria. According to the // rules for IEqualityComparer, if Equals is true, then the hash codes must // also be equal. Because equality as defined here is a simple value equality, not // reference identity, it is possible that two or more objects will produce the same // hash code. public int GetHashCode(System.IO.FileInfo fi) { string s = String.Format("{0}{1}", fi.Name, fi.Length); return s.GetHashCode(); } } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用和针对 System.Linq 命名空 间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空间的 using 指令。 z 将代码复制到项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行密集型查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/2d0ee383-7c6c-49ed-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查询目录树中的一个或多个最大的文件 (LINQ) 示例 请参见 发送反馈意见 此示例演示与文件大小(以字节为单位)相关的五种查询: z 如何检索最大文件的大小(以字节为单位)。 z 如何检索最小文件的大小(以字节为单位)。 z 如何从指定的根文件夹下的一个或多个文件夹检索 FileInfo 对象最大或最小文件。 z 如何检索一个序列,如 10 个最大文件。 z 如何按文件大小(以字节为单位)将文件分组,并忽略小于指定大小的文件。 示例 下面的示例包含五种不同的查询,这些查询演示如何根据文件大小(以字节为单位)查询和分组文件。可以轻松地修改这些示 例,以使查询基于 FileInfo 对象的某个其他属性。 C# 复制代码 class QueryBySize { static void Main(string[] args) { QueryFilesBySize(); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } private static void QueryFilesBySize() { string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\"; // Take a snapshot of the file system. // fileList is an IEnumerable var fileList = GetFiles(startFolder); //Return the size of the largest file long maxSize = (from file in fileList let len = GetFileLength(file) select len) .Max(); Console.WriteLine("The length of the largest file under {0} is {1}", startFolder, maxSize); // Return the FileInfo object for the largest file // by sorting and selecting from beginning of list System.IO.FileInfo longestFile = (from file in fileList let len = GetFileLength(file) where len > 0 orderby len descending select file) .First(); Console.WriteLine("The largest file under {0} is {1} with a length of {2} bytes", startFolder, longestFile.FullName, longestFile.Length); //Return the FileInfo of the smallest file System.IO.FileInfo smallestFile = (from file in fileList let len = GetFileLength(file) where len > 0 orderby len ascending select file).First(); Console.WriteLine("The smallest file under {0} is {1} with a length of {2} bytes", startFolder, smallestFile.FullName, smallestFile.Length); //Return the FileInfos for the 10 largest files // queryTenLargest is an IEnumerable var queryTenLargest = (from file in fileList let len = GetFileLength(file) orderby len descending select file).Take(10); Console.WriteLine("The 10 largest files under {0} are:", startFolder); foreach (var v in queryTenLargest) { Console.WriteLine("{0}: {1} bytes", v.FullName, v.Length); } 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/944f6c23-de60-4013-... 编译代码 可靠编程 若要返回一个或多个完整的 FileInfo 对象,查询必须首先检查数据源中的每个对象,然后按这些对象的 Length 属性的值排序 它们。然后查询可以返回具有最大长度的单个对象或序列。使用 First 可返回列表中的第一个元素。使用 Take(TSource) 可返 回前 n 个元素。指定降序排序顺序可将最小的元素放在列表的开头。 查询调用单独的方法获取文件大小(以字节为单位),以便消除在以下情况下可能引发的异常:自调用 GetFiles 创建 FileInfo 对象起的时间段内在另一个线程上删除了文件。虽然已创建了 FileInfo 对象,但仍会发生异常,因为 FileInfo 对象将 尝试使用第一次访问其 Length 属性时的最新大小(以字节为单位)刷新此属性。通过将此操作放在查询外的 try-catch 块 中,我们遵循了避免在查询中执行可能引起副作用的操作的规则。通常,在抑制异常时必须格外谨慎,确保没有将应用程序置 于未知状态。 // Group the files according to their size, leaving out // files that are less than 200000 bytes. var querySizeGroups = from file in fileList let len = GetFileLength(file) where len > 0 group file by (len / 100000) into fileGroup where fileGroup.Key >= 2 orderby fileGroup.Key descending select fileGroup; foreach (var filegroup in querySizeGroups) { Console.WriteLine(filegroup.Key.ToString() + "00000"); foreach (var item in filegroup) { Console.WriteLine("\t{0}: {1}", item.Name, item.Length); } } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } // This method is used to swallow the possible exception // that can be raised when accessing the FileInfo.Length property. // In this particular case, it is safe to swallow the exception. static long GetFileLength(System.IO.FileInfo fi) { long retval; try { retval = fi.Length; } catch (System.IO.FileNotFoundException) { // If a file is no longer present, // just add zero bytes to the total. retval = 0; } return retval; } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { files.Add(new System.IO.FileInfo(name)); } return files; } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用以及针 对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行大量查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/944f6c23-de60-4013-... 请参见 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/944f6c23-de60-4013-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:在目录树中查询重复文件 (LINQ) 示例 请参见 发送反馈意见 有时,多个文件夹中可能存在同名的文件。例如,在 Visual Studio 安装文件夹中,有多个文件夹包含 readme.htm 文件。此示例演示如何在指定的根文件夹中查询这样的 重复文件名。第二个示例演示如何查询其大小和创建时间也匹配的文件。 示例 C# 复制代码 class QueryDuplicateFileNames { static void Main(string[] args) { // Uncomment QueryDuplicates2 to run that query. QueryDuplicates(); // QueryDuplicates2(); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } static void QueryDuplicates() { // Change the root drive or folder if necessary string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\"; // Take a snapshot of the file system. IEnumerable fileList = GetFiles(startFolder); // used in WriteLine to keep the lines shorter int charsToSkip = startFolder.Length; // var can be used for convenience with groups. var queryDupNames = from file in fileList group file.FullName.Substring(charsToSkip) by file.Name into fileGroup where fileGroup.Count() > 1 select fileGroup; // Pass the query to a method that will // output one page at a time. PageOutput(queryDupNames); } // A Group key that can be passed to a separate method. // Override Equals and GetHashCode to define equality for the key. // Override ToString to provide a friendly name for Key.ToString() class PortableKey { public string Name { get; set; } public DateTime CreationTime { get; set; } public long Length {get; set;} public override bool Equals(object obj) { PortableKey other = (PortableKey)obj; return other.CreationTime == this.CreationTime && other.Length == this.Length && other.Name == this.Name; } public override int GetHashCode() { string str = String.Format("{0}{1}{2}", this.CreationTime, this.Length, this.Name); return str.GetHashCode(); } public override string ToString() { return String.Format("{0} {1} {2}", this.Name, this.Length, this.CreationTime); } } static void QueryDuplicates2() { // Change the root drive or folder if necessary. string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\Common7"; // Make the the lines shorter for the console display int charsToSkip = startFolder.Length; // Take a snapshot of the file system. IEnumerable fileList = GetFiles(startFolder); // Note the use of a compound key. Files that match // all three properties belong to the same group. // A named type is used to enable the query to be // passed to another method. Anonymous types can also be used // for composite keys but cannot be passed across method boundaries // var queryDupFiles = from file in fileList group file.FullName.Substring(charsToSkip) by new PortableKey{ Name=file.Name, CreationTime=file.CreationTime, Length=file.Length } into fileGroup where fileGroup.Count() > 1 select fileGroup; var list = queryDupFiles.ToList(); int i = queryDupFiles.Count(); PageOutput(queryDupFiles); } // A generic method to page the output of the QueryDuplications methods // Here the type of the group must be specified explicitly. "var" cannot // be used in method signatures. This method does not display more than one // group per page. private static void PageOutput(IEnumerable> groupByExtList) { // Flag to break out of paging loop. 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/218c27e6-3481-4870-... 编译代码 可靠编程 请参见 第一个查询使用一个简单的键确定是否匹配;这会找到同名但内容可能不同的文件。第二个查询使用复合键并根据 FileInfo 对象的三个属性来确定是否匹配。此查询 非常类似于查找同名且内容类似或相同的文件。 bool goAgain = true; // "3" = 1 line for extension + 1 for "Press any key" + 1 for input cursor. int numLines = Console.WindowHeight - 3; // Iterate through the outer collection of groups. foreach (var filegroup in groupByExtList) { // Start a new extension at the top of a page. int currentLine = 0; // Output only as many lines of the current group as will fit in the window. do { Console.Clear(); Console.WriteLine("Filename = {0}", filegroup.Key.ToString() == String.Empty ? "[none]" : filegroup.Key.ToString()); // Get 'numLines' number of items starting at number 'currentLine'. var resultPage = filegroup.Skip(currentLine).Take(numLines); //Execute the resultPage query foreach (var fileName in resultPage) { Console.WriteLine("\t{0}", fileName); } // Increment the line counter. currentLine += numLines; // Give the user a chance to escape. Console.WriteLine("Press any key to continue or the 'End' key to break..."); ConsoleKey key = Console.ReadKey().Key; if (key == ConsoleKey.End) { goAgain = false; break; } } while (currentLine < filegroup.Count()); if (goAgain == false) break; } } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { files.Add(new System.IO.FileInfo(name)); } return files; } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用以及针对 System.Linq 命名空间的 using 指 令 (C#) 或导入的命名空间 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空间的 using 指令。 z 将这段代码复制到项目中。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行密集型查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/218c27e6-3481-4870-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:查询文件夹中的文件的内容 (LINQ) 示例 请参见 发送反馈意见 此示例演示如何查询指定目录树中的所有文件、打开每个文件并检查其内容。此类技术可用于对目录树的内容创建索引或反向索引。此示例中执 行的是简单的字符串搜索。但是,可使用正则表达式执行更复杂类型的模式匹配。有关更多信息,请参见如何:将 LINQ 查询与正则表达式合并 在一起。 示例 编译代码 可靠编程 C# 复制代码 class QueryContents { public static void Main() { // Modify this path as necessary. string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\"; // Take a snapshot of the file system. IEnumerable fileList = GetFiles(startFolder); string searchTerm = @"Visual Studio"; // Search the contents of each file. // A regular expression created with the RegEx class // could be used instead of the Contains method. // queryMatchingFiles is an IEnumerable. var queryMatchingFiles = from file in fileList where file.Extension == ".htm" let fileText = GetFileText(file.FullName) where fileText.Contains(searchTerm) select file.FullName; // Execute the query. Console.WriteLine("The term \"{0}\" was found in:", searchTerm); foreach (string filename in queryMatchingFiles) { Console.WriteLine(filename); } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit"); Console.ReadKey(); } // Read the contents of the file. static string GetFileText(string name) { string fileContents = String.Empty; // If the file has been deleted since we took // the snapshot, ignore it and return the empty string. if (System.IO.File.Exists(name)) { fileContents = System.IO.File.ReadAllText(name); } return fileContents; } // This method assumes that the application has discovery // permissions for all folders under the specified path. static IEnumerable GetFiles(string path) { if (!System.IO.Directory.Exists(path)) throw new System.IO.DirectoryNotFoundException(); string[] fileNames = null; List files = new List(); fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories); foreach (string name in fileNames) { files.Add(new System.IO.FileInfo(name)); } return files; } } z 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用以及针对 System.Linq 命名空间的 using 指令 (C#) 或导入的命名空间 (Visual Basic)。在 C# 项目中,添加 System.IO 命名空间的 using 指 令。 z 将此代码复制到您的项目。 z 按 F5 编译并运行程序。 z 按任意键退出控制台窗口。 若要对多种类型的文档和文件的内容执行大量查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/f96661b4-9be7-4170-... 请参见 概念 LINQ to Objects LINQ 和文件目录 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/f96661b4-9be7-4170-... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何:使用 LINQ 查询 ArrayList 示例 请参见 发送反馈意见 在使用 LINQ 查询非泛型 IEnumerable 集合(如 ArrayList)时,您必须显式声明范围变量的类型以反映此集合中对象的特定类型。例如,如 果您具有 Student 对象的 ArrayList,则 from 子句 (C#) 或 From 子句 (Visual Basic) 应类似于: 通过指定范围变量的类型,您将 ArrayList 中的各个项强制转换为 Student。 在查询表达式中使用显式类型化的范围变量与调用 Cast(TResult) 方法等效。如果无法执行指定的强制转换,则 Cast(TResult) 引发异常。Cast (TResult) 和 OfType(TResult) 是两种对非泛型 IEnumerable 类型操作的标准查询运算符方法。 示例 请参见 复制代码 // C# var query = from Student s in arrList ... 'Visual Basic Dim query = From student As Student In arrList _ ... 下面的示例演示一个对 ArrayList 的简单查询。请注意,此示例在代码调用 Add 方法时使用对象初始值设定项,但这不是必需的。 C# 复制代码 using System; using System.Collections; using System.Linq; namespace NonGenericLINQ { public class Student { public string FirstName { get; set; } public string LastName { get; set; } public int[] Scores { get; set; } } class Program { static void Main(string[] args) { ArrayList arrList = new ArrayList(); arrList.Add( new Student { FirstName = "Svetlana", LastName = "Omelchenko", Scores = new int[] { 98, 92, 81, 60 } }); arrList.Add( new Student { FirstName = "Claire", LastName = "O’Donnell", Scores = new int[] { 75, 84, 91, 39 } }); arrList.Add( new Student { FirstName = "Sven", LastName = "Mortensen", Scores = new int[] { 88, 94, 65, 91 } }); arrList.Add( new Student { FirstName = "Cesar", LastName = "Garcia", Scores = new int[] { 97, 89, 85, 82 } }); var query = from Student student in arrList where student.Scores[0] > 95 select student; foreach (Student s in query) Console.WriteLine(s.LastName + ": " + s.Scores[0]); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } } /* Output: Omelchenko: 98 Garcia: 97 */ 概念 LINQ to Objects 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linq/html/c318b79a-fa4d-4de3-... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 请参见 发送反馈意见 LINQ to XML 提供使用 .NET Language-Integrated Query (LINQ) Framework 的内存中 XML 编程接口。LINQ to XML 使用最新的 .NET Framework 语言功能,相当于更新的和重新设计的文档对象模型 (DOM) XML 编程接口。 LINQ 系列技术提供了针对对象 (LINQ)、关系数据库 (LINQ to SQL) 和 XML (LINQ to XML) 的一致查询体验。 有关 LINQ 和 LINQ to XML 的更多信息,请参见项目 LINQ 网站(可能为英文网页)。 该网站提供有关 LINQ 项目 和相关技术的白皮书。 有关 LINQ to XML 和其他 Microsoft XML 技术的更多信息与新闻,请参见 XML 工作组博客(可能为英文网页)。 本节内容 请参见 入门 (LINQ to XML) 提供有关 LINQ to XML 的介绍性信息,包括概念概述和 System.Xml.Linq 类的概述。 编程指南 (LINQ to XML) 提供有关使用 LINQ to XML 进行编程的概念性和指导性信息。 参考 (LINQ to XML) 提供指向 LINQ to XML 管理的参考文档的链接。 示例 (LINQ to XML) 提供 LINQ to XML 的示例应用程序。 概念 Overview of LINQ to XML in Visual Basic XML in Visual Basic Language-Integrated Query (LINQ) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f0fe21e9-ee43-4a... 全部折叠 代码:C# 语言集成查询 (LINQ) 入门 (LINQ to XML) 请参见 发送反馈意见 下面的主题介绍 LINQ to XML。 本节内容 参考 请参见 主题 说明 LINQ to XML 概述 提供 LINQ to XML 技术的概述。 LINQ to XML 与 DOM 将 LINQ to XML 与文档对象模型 (DOM) 进行比较。 LINQ to XML 与其他 XML 技术 将 LINQ to XML 与其他 XML 分析和操作技术进行比较: XmlReader、XSLT、 MSXML 和 XmlLite。 参考 (LINQ to XML) 概念 LINQ to XML Overview of LINQ to XML in Visual Basic XML in Visual Basic 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6ab43316-6380-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 概述 请参见 发送反馈意见 在很多环境中,XML 已广泛采用为格式化数据的方式。 在 Web 上,在配置文件、Microsoft Office Word 文件以及 数据库中,都可以看到 XML。 LINQ to XML 经过了重新设计,是最新的 XML 编程方法。 它提供文档对象模型 (DOM) 的内存文档修改功能,支持 LINQ 查询表达式。 尽管这些查询表达式在语法上与 XPath 不同,但它们以更加类型化的方式提供类似的功能。 LINQ to XML 开发人员 什么是 LINQ to XML? LINQ to XML 是面向各种开发人员的。 对于只想完成某项工作的普通开发人员,LINQ to XML 通过提供与 SQL 相似的查询表达式,使 XML 变得更加简单。 只要稍加学习,程序员就能学会编写简洁、功能强大的查 询。 专业开发人员可以使用 LINQ to XML 来大幅提高他们的工作效率。 通过使用 LINQ to XML,他们可以编写出 表达能力更强、更为紧凑、功能更强的代码。 他们可以同时对多个数据域使用查询表达式。 LINQ to XML 是一种启用了 LINQ 的内存 XML 编程接口,使用它,可以在 .NET Framework 编程语言中处理 XML。 它将 XML 文档置于内存中,这一点很像文档对象模型 (DOM)。 您可以查询和修改 XML 文档,修改之后,可 以将其另存为文件,也可以将其序列化然后通过网络发送。 但是,LINQ to XML 与 DOM 不同: 它提供一种 新的对象模型,这是一种更轻量的模型,使用也更方便,这种模型利用了 Visual C# 2008 在语言方面的改 进。 LINQ to XML 最重要的优势是它与 Language-Integrated Query (LINQ) 的集成。 由于实现了这一集成,因 此,可以对内存 XML 文档编写查询,以检索元素和属性的集合。 LINQ to XML 的查询功能在功能上(尽管不 是在语法上)与 XPath 和 XQuery 具有可比性。 Visual C# 2008 集成 LINQ 后,可提供更强的类型化功能、 编译时检查和改进的调试器支持。 通过将查询结果用作 XElement 和 XAttribute 对象构造函数的参数,实现了一种功能强大的创建 XML 树的方 法。 这种方法称为“函数构造”,利用这种方法,开发人员可以方便地将 XML 树从一种形状转换为另一种形 状。 利用 LINQ to XML 的 LINQ 功能,可以对 XML 运行查询。 例如,您可能有一个典型 XML 采购单(如示例 XML 文件: 典型采购订单 (LINQ to XML) 所述)。 通过使用 LINQ to XML,可以运行以下查询,以获取采购 单每个项元素的部件号属性值: 在 Visual Basic 中,该查询的代码如下: 另一个示例,您可能需要一个列表,列出值大于 100 美元的项,并根据部件号排序。 若要获取此信息,可以 运行下面的查询: 在 Visual Basic 中,该查询的代码如下: LINQ to XML 提供了改进的 XML 编程接口,这一点可能与 LINQ to XML 的 LINQ 功能同样重要。 通过 LINQ to XML,对 XML 编程时,您可以实现任何预期的操作,包括: z 从文件或流加载 XML。 z 将 XML 序列化为文件或流。 z 使用函数构造从头开始创建 XML。 z 使用类似 XPath 的轴查询 XML。 C# 复制代码 IEnumerable partNos = from item in purchaseOrder.Descendants("Item") select (string) item.Attribute("PartNumber"); C# 复制代码 IEnumerable partNos = from item in purchaseOrder.Descendants("Item") where (int) item.Element("Quantity") * (decimal) item.Element("USPrice") > 100 orderby (string)item.Element("PartNumber") select item; 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/903f2350-9204-4... 创建 XML 树 请参见 z 使用 Add、Remove、ReplaceWith 和 SetValue 等方法对内存 XML 树进行操作。 z 使用 XSD 验证 XML 树。 z 使用这些功能的组合,可将 XML 树从一种形状转换为另一种形状。 创建 XML 树是否方便,这一点非常重要。 例如,若要创建一个小型 XML 树,可以编写以下 C# 代码: 在 Visual Basic 中,构造 XML 树的代码甚至更简单,因为它使用 XML 文本: Visual Basic 编译器将 XML 文本转换为对 LINQ to XML 的方法调用。 有关更多信息,请参见创建 XML 树。 C# 复制代码 XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144", new XAttribute("Type", "Home")), new XElement("phone", "425-555-0145", new XAttribute("Type", "Work")), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) ) ); 概念 入门 (LINQ to XML) Overview of LINQ to XML in Visual Basic XML in Visual Basic 参考 System.Xml.Linq 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/903f2350-9204-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 与 DOM 请参见 发送反馈意见 本节说明 LINQ to XML 和当前主导 XML 编程 API(W3C 文档对象模型 (DOM))之间的主要区别。 构造 XML 树的新方式 在 W3C DOM 中,应当从下至上生成 XML 树;即先创建文档,然后创建元素,再将元素添加到文档。 例如,下面是使用 DOM 的 Microsoft 实现 XmlDocument 创建 XML 树的典型方式: 这种编码方式不会提供很多有关 XML 树结构的可视信息。LINQ to XML 支持用此方法构造 XML 树,但也支持 另一种方法,即功能性构造法。 功能性构造法使用 XElement 和 XAttribute 构造函数生成 XML 树。 下面演示如何通过使用 LINQ to XML 功能性构造法构造相同的 XML 树: 请注意,缩进用于构造 XML 树的代码可显示基础 XML 的结构。 在 Visual Basic 中,构造 XML 树的代码甚至更简单,因为它使用 XML 文本: Visual Basic 编译器将 XML 文本转换为对 LINQ to XML 的方法调用。 如果现有一个 XML 文档并想通过它来创建 XML 树,则可以在编辑器中打开该 XML 文档,将 XML 复制到剪 贴板,在 Visual Studio 中打开 Visual Basic 模块,然后将 XML 直接粘贴到 Visual Basic 代码编辑器中。 C# 复制代码 XmlDocument doc = new XmlDocument(); XmlElement name = doc.CreateElement("Name"); name.InnerText = "Patrick Hines"; XmlElement phone1 = doc.CreateElement("Phone"); phone1.SetAttribute("Type", "Home"); phone1.InnerText = "206-555-0144"; XmlElement phone2 = doc.CreateElement("Phone"); phone2.SetAttribute("Type", "Work"); phone2.InnerText = "425-555-0145"; XmlElement street1 = doc.CreateElement("Street1"); street1.InnerText = "123 Main St"; XmlElement city = doc.CreateElement("City"); city.InnerText = "Mercer Island"; XmlElement state = doc.CreateElement("State"); state.InnerText = "WA"; XmlElement postal = doc.CreateElement("Postal"); postal.InnerText = "68042"; XmlElement address = doc.CreateElement("Address"); address.AppendChild(street1); address.AppendChild(city); address.AppendChild(state); address.AppendChild(postal); XmlElement contact = doc.CreateElement("Contact"); contact.AppendChild(name); contact.AppendChild(phone1); contact.AppendChild(phone2); contact.AppendChild(address); XmlElement contacts = doc.CreateElement("Contacts"); contacts.AppendChild(contact); doc.AppendChild(contacts); C# 复制代码 XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144", new XAttribute("Type", "Home")), new XElement("phone", "425-555-0145", new XAttribute("Type", "Work")), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) ) ); 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/19b5ed02-feb2-4... 直接使用 XML 元素 名称和命名空间的简化处理 对加载 XML 的静态方法支持 移除对 DTD 构造的支持 对片段的支持 对 XPathNavigator 的支持 对空白和缩进的支持 有关更多信息,请参见创建 XML 树。 在使用 XML 编程时,主要关注的通常是 XML 元素,也可能关注属性。 在 LINQ to XML 中,可以直接使用 XML 元素和属性。 例如,可以执行以下操作: z 创建 XML 元素而根本不使用文档对象。 当必须使用 XML 树的片段时,这可简化编程。 z 直接从 XML 文件加载 T:System.Xml.Linq.XElement 对象。 z 将 T:System.Xml.Linq.XElement 对象序列化为文件或流。 比较而言,W3C DOM 中的 XML 文档用作 XML 树的逻辑容器。 在 DOM 中,必须在 XML 文档的上下文中创 建 XML 节点,包括元素和属性。 下面是在 DOM 中创建一个 name 元素的代码片段: 如果要跨多个文档使用某个元素,则必须跨文档导入节点。 使用 LINQ to XML 可以避免这一不必要的复杂 层。 使用 LINQ to XML 时,仅在文档的根级别添加注释或处理说明时,才需使用 XDocument 类。 C# 复制代码 XmlDocument doc = new XmlDocument(); XmlElement name = doc.CreateElement("Name"); name.InnerText = "Patrick Hines"; doc.AppendChild(name); 处理名称、命名空间和命名空间前缀通常是 XML 编程的复杂部分。LINQ to XML 完全不需要处理命名空间前 缀,从而简化了名称和命名空间。 可以轻松控制命名空间前缀。 但如果您决定不显式控制命名空间前缀,则 在序列化时,LINQ to XML 将会分配命名空间前缀(如果需要)或使用默认命名空间进行序列化。 如果使用 默认命名空间,则生成的文档中将没有命名空间前缀。 LINQ to XML 也不需要用户理解什么是 NameTable 以及其使用方式。 有关更多信息,请参见使用 XML 命名空间。 DOM 的另一个问题是它不允许您更改节点的名称;您必须创建新节点并将所有子节点复制到此节点,从而会 失去原始节点标识。LINQ to XML 允许对节点设置 XName 属性,因此可避免此问题。 LINQ to XML 允许您通过使用静态方法而不是实例方法来加载 XML。 这简化了加载和分析。 通过移除对实体和实体引用的支持,LINQ to XML 进一步简化了 XML 编程。 实体因管理复杂而很少使用。 移除对它们的支持可提高性能并简化编程接口。 在填充 LINQ to XML 树时,会展开所有 DTD 实体。 LINQ to XML 未提供 XmlDocumentFragment 类的等效项。 但在很多情况下,XmlDocumentFragment 概 念都可以通过执行类型化为XNode 的 IEnumerable(T) 或 XElement 的 IEnumerable(T) 的查询来进行处理。 LINQ to XML 通过 System.Xml.XPath 命名空间中的扩展方法提供对 XPathNavigator 的支持。 有关更多信 息,请参见 System.Xml.XPath.Extensions。 LINQ to XML 处理空白的方式比 DOM 更简单。 一种常用方案是读取缩进的 XML,在内存中创建一个没有任何空白文本节点(即不保留空白)的 XML 树,对 该 XML 执行某些操作,然后保存带缩进的 XML。 在序列化带格式的 XML 时,只保留 XML 树中有意义的空 白。 这是 LINQ to XML 的默认行为。 另一个常见的方案是读取和修改已经有意缩进的 XML。 您可能不想以任何方式更改这种缩进。 在 LINQ to XML 中,如果在加载或解析 XML 时保留空白,并在序列化 XML 时禁用格式设置,就可以实现此目的。 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/19b5ed02-feb2-4... 对批注的支持 对架构信息的支持 请参见 LINQ to XML 将空白存储为 XText 节点,而不像 DOM 那样具有专门的 Whitespace 节点类型。 LINQ to XML 元素支持可扩展的批注集。 有关更多信息,请参见 LINQ to XML 批注。 这对于跟踪有关元素的 杂项信息(如架构信息、元素是否绑定到 UI 或应用程序特定的任何其他信息)很有用。 LINQ to XML 通过 System.Xml.Schema 命名空间中的扩展方法提供对 XSD 验证的支持。 您可以验证 XML 树是否符合 XSD。 您可以用架构验证后信息集 (PSVI) 填充 XML 树。 有关更多信息,请参见如何: 使用 XSD 进行验证 (LINQ to XML)和 Extensions。 概念 入门 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/19b5ed02-feb2-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 与其他 XML 技术 请参见 发送反馈意见 本主题将 LINQ to XML 与下面的 XML 技术进行比较: XmlReader、XSLT、MSXML 和 XmlLite。 了解这些信息,有 助于确定要使用哪种技术。 有关比较 LINQ to XML 与文档对象模型 (DOM) 的信息,请参见 LINQ to XML 与 DOM。 LINQ to XML 与 XmlReader LINQ to XML 与 XSLT LINQ to XML 与 MSXML LINQ to XML 与 XmlLite XmlReader 是一种快速的只进非缓存分析器。 LINQ to XML 在 XmlReader 基础之上实现,它们紧密集成在一起。 虽然如此,还是可以独立地使用 XmlReader。 尽管它们是重叠的,但 LINQ to XML 和 XmlReader 的使用场合不同。 例如,假设要生成一项 Web 服务,该服务每秒将分析几百个 XML 文档,而这些文档具有相同的结构,因 此,只需编写一种代码实现即可对 XML 进行分析。 这种情况下,可能希望以独立的方式使用 XmlReader。 相反,如果要生成一个系统,用以分析多种小型 XML 文档,而这些文档各不相同,则可能希望利用 LINQ to XML 提高工作效率。 LINQ to XML 和 XSLT 都提供丰富的 XML 文档转换功能。 XSLT 是基于规则的声明性方法。 XSLT 高级程序 员以函数编程方式编写 XSLT,这种方式强调无状态方法,使用实现后无副作用的纯函数进行重构。 许多开 发人员还不熟悉这种基于规则的方法(即函数方法),需要付出许多努力才能掌握这项技术。 XSLT 是非常高效的系统,可以生成高性能的应用程序。 一些大型 Web 公司使用 XSLT 从 XML(提取自很多 数据存储区)生成 HTML。 托管 XSLT 引擎将 XSLT 编译为 CLR 代码,在某些情况下,其性能甚至比本机 XSLT 引擎还要好。 但是,XSLT 没有利用许多开发人员都具备的 C# 和 Visual Basic 知识。 它要求开发人员用一种不同的复杂编 程语言来编写代码。 如果使用两种不同的非集成开发系统,例如 C#(或 Visual Basic)和 XSLT),软件系 统的开发和维护会更加困难。 掌握 LINQ to XML 查询表达式之后,LINQ to XML 转换就是一项功能强大、易于使用的技术。 本质上,XML 文档是这样形成的:使用函数构造,从各种源提取数据,动态构造 XElement 对象,再将全部内容组合到一个 新 XML 树中。 经过这种转换,可以生成一个全新的文档。 相对来说,在 LINQ to XML 中构造转换比较容 易、直观,编写出的代码可读性也较强。 这样可以减少开发和维护成本。 LINQ to XML 不是用来替代 XSLT 的。 对于复杂的以文档为中心的 XML 转换,XSLT 仍是很好的工具,如果 文档结构的定义不完备,更是如此。 XSLT 的优势在于符合 W3C 标准。 如果要求只使用符合标准的技术,XSLT 可能更为合适。 XSLT 是 XML,因此可以以编程方式进行操作。 MSXML 是基于 COM 的技术,用于处理 Microsoft Windows 提供的 XML。 MSXML 提供 DOM 的本机实现,并 且支持 XPath 和 XSLT。 它还包含基于事件的 SAX2 非缓存分析器。 MSXML 性能良好,默认情况下,在大多数情况中都是安全的,在 Internet Explorer 中可以利用此功能来执行 AJAX 式应用程序中的客户端 XML 处理。 在任何支持 COM 的编程语言(包括 C++、JavaScript 和 Visual Basic 6.0)中,都可以使用 MSXML。 建议不要在基于公共语言运行库 (CLR) 的托管代码中使用 MSXML。 XmlLite 是一种只进非缓存提取型分析器。 开发人员主要将 XmlLite 用于 C++。 建议开发人员不要将 XmlLite 用于托管代码。 XmlLite 的主要优势在于它是快速的轻量 XML 分析器,在大多数方案中都是安全的。 它可能受到的威胁很 少。 如果必须分析不受信任的文档,并且要预防受到拒绝服务或数据暴露等攻击,则 XmlLite 可能是很好的 选择。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7ba1eecf-f09a-42... 请参见 XmlLite 未与 Language-Integrated Query (LINQ) 集成。 它不会使程序员的工作效率得到提高,而工作效率提 高正是 LINQ 背后的推动力量。 概念 入门 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7ba1eecf-f09a-42... 全部折叠 代码:C# 语言集成查询 (LINQ) 编程指南 (LINQ to XML) 请参见 发送反馈意见 本节提供有关使用 LINQ to XML 进行编程的概念性和指导性信息。 本文档的目标读者 本节内容 请参见 本文档面向已经了解 C# 以及 .NET Framework 的一些基本知识的开发人员。 本文档的目的在于降低各类开发人员对 LINQ to XML 的使用难度。LINQ to XML 使 XML 编程变得更容易。 您 无需成为一名专家级开发人员就可以使用它。 LINQ to XML 非常依赖于泛型类。 因此,了解泛型类的使用非常重要。 此外,熟悉作为参数化类型声明的委 托也很有帮助。 如果您不熟悉 C# 泛型类,请参见Generic Classes (C# Programming Guide)。 主题 说明 LINQ to XML 编程概述 提供对 LINQ to XML 类的概述以及有关以下三个最重要类的详细信息: XElement、 XAttribute 和 XDocument。 创建 XML 树 提供有关创建 XML 树的概念性和基于任务的信息。 可以通过使用函数构造,或通过从字 符串或文件解析 XML 文本来创建 XML 树。 也可以使用 XmlReader 来填充 XML 树。 使 用 Visual Basic 创建 XML 树与使用 C# 创建 XML 树有很大差异。 这些主题描述如何使 用这两种语言创建 XML 树。 使用 XML 命名空间 提供有关创建使用命名空间的 XML 树的详细信息。 序列化 XML 树 描述序列化 XML 树的多种方法,并给出选择使用方法的指导。 LINQ to XML 轴 列举并介绍 LINQ to XML 轴方法,您必须了解轴方法才能编写 LINQ to XML 查询。 查询 XML 树 提供查询 XML 树的常见示例。 修改 XML 树 (LINQ to XML) 如同文档对象模型 (DOM) 一样,LINQ to XML 也允许您就地修改 XML 树。 高级 LINQ to XML 编程 提供有关批注、事件、流处理和其他高级方案的信息。 LINQ to XML 安全性 描述与 LINQ to XML 相关的安全问题并提供减小安全隐患的一些指导。 示例 XML 文档 (LINQ to XML) 包含本文档中许多示例使用的示例 XML 文档。 概念 入门 (LINQ to XML) LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d92b5cb3-f0c7-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 编程概述 请参见 发送反馈意见 这些主题提供有关 LINQ to XML 类的高级概述信息,以及有关三个最重要类的详细信息。 本节内容 请参见 主题 说明 LINQ to XML 编程方法概 述 总览介绍编写 LINQ to XML 应用程序的两种主要方法。 LINQ to XML 类概述 提供 LINQ to XML 类的概述。 XElement 类概述 介绍 XElement 类,该类表示 XML 元素。XElement 是 LINQ to XML 类层次 结构中的基础类之一。 XAttribute 类概述 介绍 XAttribute 类,该类表示 XML 属性。 XDocument 类概述 介绍 XDocument 类,该类表示 XML 文档。 如何: 生成 LINQ to XML 示例 包含生成 LINQ to XML 示例所需的 Using 指令和导入语句。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/92fd5b4a-8735-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 编程方法概述 请参见 发送反馈意见 XML 应用程序有多种类型: z 有些应用程序采用源 XML 文档并生成与源文档形状不同的新 XML 文档。 z 有些应用程序采用源 XML 文档并生成格式完全不同的结果文档,如 HTML 或 CSV 文本文件。 z 有些应用程序采用源 XML 文档并将记录插入数据库。 z 有些应用程序采用另一个源(如数据库)中的数据并从该数据创建 XML 文档。 这些并不是所有的 XML 应用程序类型,但它们是 XML 程序员必须实现的一组有代表性的功能类型。 对于所有这些类型应用程序,开发人员可以采用两种对比方法: z 使用声明性方法的函数构造法。 z 使用过程代码的内存中 XML 树修改法。 LINQ to XML 同时支持这两种方法。 使用函数方法时,基本要旨是编写可采用源文档并生成具有所需形状的全新结果文档的转换。 就地修改 XML 树时,基本要旨是编写可遍历内存中 XML 树节点并在其中导航以便根据需要插入、删除和修改节点 的代码。 可以对任一方法使用 LINQ to XML。 使用的类相同,在某些情况下使用的方法也相同。 但这两种方法的结构和目 标却大相径庭。 这两种方法通常具有不同的性能配置文件。 在不同情况下,其中一种方法通常具有更好的性能,使用更多或更少的 内存。 另外,其中一种方法会更容易编写并生成更容易维护的代码。 若要查看这两种相对比的方法,请参见内存中 XML 树修改与函数构造 (LINQ to XML)。 有关编写函数转换的教程,请参见 XML 的纯函数转换。 请参见 概念 LINQ to XML 编程概述 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3a1afdb4-411c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 类概述 请参见 发送反馈意见 本主题提供 System.Xml.Linq 命名空间中 LINQ to XML 类的列表并提供每个类的简短说明。 LINQ to XML 类 XAttribute 类 XAttribute 表示一个 XML 属性。 有关详细信息和示例,请参见 XAttribute 类概述。 XCData 类 XCData 表示一个 CDATA 文本节点。 XComment 类 XComment 表示一个 XML 注释。 XContainer 类 XContainer 是适用于可能具有子节点的所有节点的抽象基类。 下面的类派生自 XContainer 类: z XElement z XDocument XDeclaration 类 XDeclaration 表示一个 XML 声明。 XML 声明用于声明 XML 版本和文档的编码。 此外,XML 声明还指定 XML 文档是否为独立文档。 XDocument 类 XDocument 表示一个 XML 文档。 有关详细信息和示例,请参见 XDocument 类概述。 XDocumentType 类 XDocumentType 表示一个 XML 文档类型定义 (DTD)。 XElement 类 XElement 表示一个 XML 元素。 有关详细信息和示例,请参见 XElement 类概述。 XName 类 XName 表示元素 (XElement) 和属性 (XAttribute) 的名称。 有关详细信息和示例,请参见 XDocument 类概 述。 LINQ to XML 旨在使 XML 名称尽可能简单。 XML 名称由于复杂而通常被视为 XML 中的高级主题。 有证据证 明,这种复杂性不是由开发人员编程时通常使用的命名空间造成的,而是由命名空间前缀造成的。 使用命名 空间前缀可以减少输入 XML 时需要的击键数或使 XML 更具可读性。 但前缀通常只是使用完整 XML 命名空 间的快捷方式,在多数情况下并不需要。LINQ to XML 通过将所有前缀解析为其对应的 XML 命名空间来简化 XML 名称。 如果需要,可以通过 GetPrefixOfNamespace 方法可以使用前缀。 如果有必要,可以控制命名空间前缀。 在某些情况下,如果使用的是其他 XML 系统(如 XSLT 或 XAML), 则需要控制命名空间前缀。 例如,如果 XPath 表达式使用 XSLT 样式表中嵌入的命名空间前缀,则将需要确 保使用与 XPath 表达式中使用的前缀相匹配的命名空间前缀来序列化 XML 文档。 XNamespace 类 XNamespace 表示 XElement 或 XAttribute 的命名空间。 命名空间是 XName 的一个组件。 XNode 类 XNode 是一个抽象类,它表示 XML 树的节点。 下面的类派生自 XNode 类: z XText 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/1a603f1d-060c-4... 请参见 z XContainer z XComment z XProcessingInstruction z XDocumentType XNodeDocumentOrderComparer 类 XNodeDocumentOrderComparer 提供用于比较节点的文档顺序的功能。 XNodeEqualityComparer 类 XNodeEqualityComparer 提供用于比较节点的值是否相等的功能。 XObject 类 XObject 是 XNode 和 XAttribute 的抽象基类。 它提供批注和事件功能。 XObjectChange 类 XObjectChange 指定对 XObject 引发事件时的事件类型。 XObjectChangeEventArgs 类 XObjectChangeEventArgs 为 Changing 和 Changed 事件提供数据。 XProcessingInstruction 类 XProcessingInstruction 表示一个 XML 处理指令。 处理指令将信息传递给处理 XML 的应用程序。 XText 类 XText 表示一个文本节点。 多数情况下都不必使用此类。 此类主要用于混合内容。 概念 LINQ to XML 编程概述 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/1a603f1d-060c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XElement 类概述 请参见 发送反馈意见 XElement 类是 LINQ to XML 中的基础类之一。 它表示一个 XML 元素。 可以使用该类创建元素;更改元素内容; 添加、更改或删除子元素;向元素中添加属性;或以文本格式序列化元素内容。 还可以与 System.Xml 中的其他类 (例如 XmlReader、XmlWriter 和 XslCompiledTransform)进行互操作。 XElement 功能 本主题描述 XElement 类提供的功能。 构造 XML 树 可以使用各种方式构造 XML 树,包括以下方式: z 可以在代码中构造 XML 树。 有关更多信息,请参见创建 XML 树。 z 可以从包括 TextReader、文本文件或 Web 地址 (URL) 在内的各种源解析 XML。 有关更多信息,请参见 分析 XML。 z 可以使用 XmlReader 来填充树。 有关更多信息,请参见 ReadFrom。 z 如果您有一个可以将内容写入 XmlWriter 的模块,则可以使用 CreateWriter 方法来创建编写器,将该编 写器传递到该模块,然后使用写入 XmlWriter 的内容来填充 XML 树。 但是,创建 XML 树的最常见的方法如下: 另一个创建 XML 树的十分常用的方法是使用 LINQ 查询的结果来填充 XML 树,如下面的示例所示: 此示例产生以下输出: 序列化 XML 树 C# 复制代码 XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144"), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) ) ); C# 复制代码 XElement srcTree = new XElement("Root", new XElement("Element", 1), new XElement("Element", 2), new XElement("Element", 3), new XElement("Element", 4), new XElement("Element", 5) ); XElement xmlTree = new XElement("Root", new XElement("Child", 1), new XElement("Child", 2), from el in srcTree.Elements() where (int)el > 2 select el ); Console.WriteLine(xmlTree); Xml 复制代码 1 2 3 4 5 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d35180fe-7016-4... 请参见 可以将 XML 树序列化为 File、TextWriter 或 XmlWriter。 有关更多信息,请参见序列化 XML 树。 通过轴方法检索 XML 数据 可以使用轴方法检索属性、子元素、子代元素和上级元素。LINQ 查询对轴方法进行操作,并提供了多种灵活 而有效的方法导航和处理 XML 树。 有关更多信息,请参见 LINQ to XML 轴。 查询 XML 树 可以编写从 XML 树提取数据的 LINQ 查询。 有关更多信息,请参见查询 XML 树。 修改 XML 树 可以通过各种方式修改元素,例如更改元素的内容或属性。 还可以从元素的父级移除元素。 有关更多信息,请参见修改 XML 树 (LINQ to XML)。 概念 LINQ to XML 编程概述 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d35180fe-7016-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XAttribute 类概述 请参见 发送反馈意见 属性是与元素关联的名称/值对。 XAttribute 类表示 XML 属性。 概述 XAttribute 构造函数 使用 LINQ to XML 中的属性,与使用元素非常相似。 它们的构造函数相似。 用于检索它们的集合的方法相 似。 属性集合的 LINQ 查询表达式与元素集合的 LINQ 查询表达式看起来非常相似。 将属性添加到元素中的顺序会保留下来。 也就是说,当循环访问属性时,所见到的属性顺序与它们的添加顺 序相同。 下面的 XAttribute 类构造函数是您将最常使用的构造函数之一: 创建具有属性的元素 下面的代码演示创建包含属性的元素的常见任务: 在 Visual Basic 中,您可以使用 XML 文本: 此示例产生以下输出: 属性的函数构造 可以构造与 XElement 对象的构造一致的 XAttribute 对象,如下所示: 此示例产生以下输出: 构造函数 说明 XAttribute(XName name, object content) 创建一个 XAttribute 对象。 name 参数指定属性的名称; content 指定属性的内容。 C# 复制代码 XElement phone = new XElement("Phone", new XAttribute("Type", "Home"), "555-555-5555"); Console.WriteLine(phone); Xml 复制代码 555-555-5555 C# 复制代码 XElement c = new XElement("Customers", new XElement("Customer", new XElement("Name", "John Doe"), new XElement("PhoneNumbers", new XElement("Phone", new XAttribute("type", "home"), "555-555-5555"), new XElement("Phone", new XAttribute("type", "work"), "666-666-6666") ) ) ); Console.WriteLine(c); Xml 复制代码 John Doe 555-555-5555 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ab0fed32-994c-4... 请参见 属性不是节点 属性与元素之间有些区别。XAttribute 对象不是 XML 树中的节点。 它们是与 XML 元素关联的名称/值对。 与文档对象模型 (DOM) 相比,这更加贴切地反映了 XML 结构。 虽然 XAttribute 对象实际上不是 XML 树的 节点,但使用 XAttribute 对象与使用 XElement 对象非常相似。 这一区别仅对编写在节点级使用 XML 树的代码的开发人员特别重要。 许多开发人员不会关心这种区别。 666-666-6666 概念 LINQ to XML 编程概述 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ab0fed32-994c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XDocument 类概述 请参见 发送反馈意见 本主题介绍 XDocument 类。 XDocument 类概述 Xdocument 的组件 在没有 Xdocument 的情况下使用 XElement 使用 XDocument XDocument 类包含有效的 XML 文档所需的信息。 其中包括 XML 声明、处理指令和注释。 请注意,如果需要 XDocument 类提供的特定功能,您只需创建 XDocument 对象。 在很多情况下,可以直接 使用 XElement。 直接使用 XElement 是一种比较简单的编程模型。 XDocument 是从 XContainer 派生的。 因此,它可以包含子节点。 但是,XDocument 对象只能有一个子 XElement 节点。 这反映了 XML 标准,即在 XML 文档中只能有一个根元素。 XDocument 可以包含以下元素: z 一个 XDeclaration 对象。XDeclaration 使您能够指定 XML 声明的相关部分: XML 版本、文档的编码,以 及 XML 文档是否是独立的。 z 一个 XElement 对象。 这是 XML 文档的根节点。 z 任意数目的 XProcessingInstruction 对象。 处理指令将信息传递给处理 XML 的应用程序。 z 任意数目的 XComment 对象。 注释将与根元素同级。 XComment 对象不能是列表中的第一个参数,因 为 XML 文档以注释开头无效。 z 一个用于 DTD 的 XDocumentType。 序列化 XDocument 时,即使 XDocument.Declaration 为 null,输出也将具有 XML 声明,前提是编写器已 经将 Writer.Settings.OmitXmlDeclaration 设置为 false(默认值)。 默认情况下,LINQ to XML 将版本设置为“1.0”,将编码设置为“utf-8”。 如上所述,XElement 类是 LINQ to XML 编程接口中的主类。 在很多情况下,您的应用程序不需要您创建文 档。 通过使用 XElement 类,可以创建 XML 树,向它添加其他 XML 树,修改 XML 树并进行保存。 若要构造一个 XDocument,可使用函数构造,正如您构造 XElement 对象那样。 下面的代码创建一个 XDocument 对象及其关联的包含对象。 当您检查文件 test.xml 时, 会得到以下输出: C# 复制代码 XDocument d = new XDocument( new XComment("This is a comment."), new XProcessingInstruction("xml-stylesheet", "href='mystyle.css' title='Compact' type='text/css'"), new XElement("Pubs", new XElement("Book", new XElement("Title", "Artifacts of Roman Civilization"), new XElement("Author", "Moreno, Jordao") ), new XElement("Book", new XElement("Title", "Midieval Tools and Implements"), new XElement("Author", "Gazit, Inbar") ) ), new XComment("This is another comment.") ); d.Declaration = new XDeclaration("1.0", "utf-8", "true"); Console.WriteLine(d); d.Save("test.xml"); Xml 复制代码 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/90f78331-1be8-4... 请参见 Artifacts of Roman Civilization Moreno, Jordao Midieval Tools and Implements Gazit, Inbar 概念 LINQ to XML 编程概述 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/90f78331-1be8-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 生成 LINQ to XML 示例 示例 请参见 发送反馈意见 本文档中的各代码段和示例使用多个命名空间中的类和类型。 在编译 C# 代码时,您需要提供相应的 using 指 令。 在编译 Visual Basic 代码时,您需要提供相应的 Imports 语句。 示例 请参见 下面的代码包含 C# 示例需要生成和运行的 using 指令。 并非每个示例都需要所有 using 指令。 下面的代码包含 Visual Basic 示例需要生成和运行的 Imports 语句。 并非每个示例都需要所有 Imports 语 句。 C# 复制代码 using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.Linq; using System.Xml; using System.Xml.Linq; using System.Xml.Schema; using System.Xml.XPath; using System.Xml.Xsl; using System.IO; using System.Threading; using System.Reflection; using System.IO.Packaging; 概念 LINQ to XML 编程概述 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/193b641a-9a00-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 创建 XML 树 请参见 发送反馈意见 最常见的 XML 任务之一是构造 XML 树。 本节介绍多种创建 XML 树的方法。 本节内容 请参见 主题 说明 函数构造 (LINQ to XML) 提供对 LINQ to XML 中的功能构造的概述。 功能构造使您能在单条语句 中创建全部或部分 XML 树。 演示如何在构造 XML 树时嵌入查询。 使用 C# 创建 XML 树 (LINQ to XML) 演示如何在 C# 中创建树。 Visual Basic 中的 XML 文本简 介 快速介绍如何使用 XML 文本在 Visual Basic 中创建树。 本主题包括到 XML 文本的 Visual Basic 文档的链接。 克隆与附加 演示从现有 XML 树(先克隆节点然后添加)添加节点与添加没有父节点 的节点(只是简单地附加)之间的差别。 分析 XML 演示如何从不同的源分析 XML。LINQ to XML 在 XmlReader 的上层,后 者用于分析 XML。 如何: 用 XmlWriter 填充 XML 树 (LINQ to XML) 演示如何使用 XmlWriter 填充 XML 树。 如何: 使用 XSD 进行验证 (LINQ to XML) 演示如何使用 XSD 验证 XML 树。 XElement 和 XDocument 对 象的有效内容 详细描述可以传递给构造函数的有效参数,以及用于向元素和文档添加 内容的方法。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/9e2abee4-6415-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 函数构造 (LINQ to XML) 请参见 发送反馈意见 LINQ to XML 为创建 XML 元素提供了一种称为“函数构造”的有效方式。 函数构造是指在单个语句中创建 XML 树的能 力。 启用函数构造的 LINQ to XML 编程接口有几个重要功能: z XElement 构造函数可以对内容采用多种类型的参数。 例如,可以传递另一个 XElement 对象,该对象将成为一个子元 素。 可以传递一个 XAttribute 对象,该对象将成为该元素的一个属性。 也可以传递任何其他类型的对象,该对象将 转换为字符串并成为该元素的文本内容。 z XElement 函数采用类型为 Object 的 params 数组,因此可以向该构造函数传递任意数目的对象。 这使您可以创建 具有复杂内容的元素。 z 如果对象实现 IEnumerable(T),则枚举对象中的集合,并添加集合中的所有项。 如果集合包含 XElement 或 XAttribute 对象,则单独添加集合中的每一项。 这一功能很重要,因为它允许您将 LINQ 查询的结果传递给构造函 数。 这些功能使您能够编写代码来创建 XML 树。 下面是一个示例: 这些功能还使您能够在创建 XML 树时,编写使用 LINQ 查询结果的代码,如下所示: 在 Visual Basic 中,通过 XML 文本可以完成同样的操作。 本示例生成以下输出: 请参见 C# 复制代码 XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144"), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) ) ); C# 复制代码 XElement srcTree = new XElement("Root", new XElement("Element", 1), new XElement("Element", 2), new XElement("Element", 3), new XElement("Element", 4), new XElement("Element", 5) ); XElement xmlTree = new XElement("Root", new XElement("Child", 1), new XElement("Child", 2), from el in srcTree.Elements() where (int)el > 2 select el ); Console.WriteLine(xmlTree); Xml 复制代码 1 2 3 4 5 概念 创建 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/169869e4-6846-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用 C# 创建 XML 树 (LINQ to XML) 请参见 发送反馈意见 本节提供有关使用 C# 来创建 XML 树的信息。 有关使用 LINQ 查询结果作为 XElement 的内容的信息,请参见函数构造 (LINQ to XML)。 构造元素 XElement 构造函数 通过 XElement 和 XAttribute 构造函数的签名,可以将元素或属性的内容作为参数传递到构造函数。 由于其中一个构造函 数使用可变数目的参数,因此可以传递任意数目的子元素。 当然,这些子元素中的每一个都可以包含它们自己的子元素。 对任意元素,可以添加任意多个属性。 在添加 XNode(包括 XElement)或 XAttribute 对象时,如果新内容没有父级,则直接将这些对象附加到 XML 树中。 如果 新内容已经有父级,并且是另一 XML 树的一部分,则克隆新内容,并将新克隆的内容附加到 XML 树。 本主题最后一个示 例对此进行了演示。 若要创建 contactsXElement,可以使用下面的代码: 如果正确缩进,则构造 XElement 对象的代码十分类似于基础 XML 的结构。 Visual Basic 具有另一种创建 XML 树的方法: 可以直接在 Visual Basic 程序中将 XML 作为 XML 文本嵌入。 C# 复制代码 XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144"), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) ) ); XElement 类使用下面的构造函数用于函数构造。 请注意,对于 XElement,存在其他一些构造函数,但是由于它们不用于 函数构造,因此没有在此列出。 content 参数极其灵活。 它支持作为 XElement 的有效子对象的任何类型的对象。 下面的规则适用于在此参数中传递的不 同类型的对象: z 字符串添加为文本内容。 z XElement 添加为子元素。 z XAttribute 添加为属性。 z XProcessingInstruction、XComment 或 XText 添加为子内容。 z 枚举 IEnumerable,并且这些规则递归应用于结果。 z 对任何其他类型,调用其 ToString 方法,结果添加为文本内容。 创建包含内容的 XElement 可以使用单个方法调用来创建一个包含简单内容的 XElement。 为此,请指定内容作为第二个参数,如下所示: 此示例产生以下输出: 构造函数 说明 XElement(XName name, object content) 创建一个 XElement。 name 参数指定元素的名称;content 指定元素的 内容。 XElement(XName name) 创建一个 XElement,其 XName 初始化为指定名称。 XElement(XName name, params object [] content) 创建一个 XElement,其 XName 初始化为指定名称。 从参数列表的内容 创建属性和/或子元素。 C# 复制代码 XElement n = new XElement("Customer", "Adventure Works"); Console.WriteLine(n); Xml 复制代码 Adventure Works 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/dd24d4c8-272f-4... 可以将任意类型的对象作为内容进行传递。 例如,下面的代码创建一个包含浮点数作为内容的元素: 此示例产生以下输出: 该浮点数被装箱并传递给构造函数。 装箱的数字转换为字符串,然后用作元素的内容。 创建具有子元素的 XElement 如果传递 XElement 类的一个实例作为内容参数,则构造函数将创建一个具有子元素的元素: 此示例产生以下输出: 创建具有多个子元素的 XElement 可以传递多个 XElement 对象作为内容。 每个 XElement 对象都作为子元素包含进来。 此示例产生以下输出: 将上面的示例进行扩展,可以创建整个 XML 树,如下所示: 此示例产生以下输出: C# 复制代码 XElement n = new XElement("Cost", 324.50); Console.WriteLine(n); Xml 复制代码 324.5 C# 复制代码 XElement shippingUnit = new XElement("ShippingUnit", new XElement("Cost", 324.50) ); Console.WriteLine(shippingUnit); Xml 复制代码 324.5 C# 复制代码 XElement address = new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ); Console.WriteLine(address); Xml 复制代码
123 Main St Mercer Island WA 68042
C# 复制代码 XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144"), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042") ) ) ); Console.WriteLine(contacts); Xml 复制代码 Patrick Hines 206-555-0144
123 Main St Mercer Island 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/dd24d4c8-272f-4... 请参见 创建空元素 若要创建空 XElement,请不要将任何内容传递给构造函数。 下面的示例创建一个空元素: 此示例产生以下输出: 附加与克隆 前面提到,在添加 XNode(包括 XElement)或 XAttribute 对象时,如果新内容没有父级,则直接将这些对象附加到 XML 树。 如果新内容已经有父级,并且是另一 XML 树的一部分,则克隆新内容,并将新克隆的内容附加到 XML 树。 此示例产生以下输出: WA 68042
C# 复制代码 XElement n = new XElement("Customer"); Console.WriteLine(n); Xml 复制代码 C# 复制代码 // Create a tree with a child element XElement xmlTree1 = new XElement("Root", new XElement("Child1", 1) ); // Create an element that is not parented XElement child2 = new XElement("Child2", 2); // Create a tree and add Child1 and Child2 to it XElement xmlTree2 = new XElement("Root", xmlTree1.Element("Child1"), child2 ); // Compare Child1 identity. Console.WriteLine("Child1 was {0}", xmlTree1.Element("Child1") == xmlTree2.Element("Child1") ? "attached" : "cloned"); // Compare Child2 identity. Console.WriteLine("Child2 was {0}", child2 == xmlTree2.Element("Child2") ? "attached" : "cloned"); 复制代码 Child1 was cloned Child2 was attached 概念 创建 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/dd24d4c8-272f-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 克隆与附加 请参见 发送反馈意见 在将 XNode(包括 XElement)或 XAttribute 对象添加到新树中时,如果新内容没有父级,则直接将这些对象附加 到 XML 树中。 如果新内容已经有父级,并且是另一 XML 树的一部分,则克隆新内容,并将新克隆的内容附加到 XML 树。 示例 请参见 下面的代码演示将有父级的元素添加到树中,以及将没有父级的元素添加到树中时的行为。 此示例产生以下输出: C# 复制代码 // Create a tree with a child element XElement xmlTree1 = new XElement("Root", new XElement("Child1", 1) ); // Create an element that is not parented XElement child2 = new XElement("Child2", 2); // Create a tree and add Child1 and Child2 to it XElement xmlTree2 = new XElement("Root", xmlTree1.Element("Child1"), child2 ); // compare Child1 identity Console.WriteLine("Child1 was {0}", xmlTree1.Element("Child1") == xmlTree2.Element("Child1") ? "attached" : "cloned"); // compare Child2 identity Console.WriteLine("Child2 was {0}", child2 == xmlTree2.Element("Child2") ? "attached" : "cloned"); 复制代码 Child1 was cloned Child2 was attached 概念 创建 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fff87be4-ae44-4c... 全部折叠 代码:C# 语言集成查询 (LINQ) 分析 XML 请参见 发送反馈意见 本节中的主题介绍如何分析 XML 文档。 本节内容 请参见 主题 说明 如何: 分析字符串 演示如何分析字符串从而创建 XML 树。 如何: 从文件加载 XML 演示如何使用 Load 方法从 URI 加载 XML。 加载或分析 XML 时保留空白 介绍如何在加载 XML 树时控制 LINQ to XML 的空白行为。 如何: 捕捉分析错误 演示如何检测格式不正确或无效的 XML。 如何: 从 XmlReader 创建树 演示如何从 XmlReader 直接创建 XML 树。 如何: 流式处理 XmlReader 中的 XML 片段 演示如何使用 XmlReader 对 XML 片段进行流式处理。 如果必须处理很大的 XML 文件,将整个 XML 树加载到内存可能不可 行。 这时,可以对 XML 片段进行流式处理。 概念 创建 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/8bdfdca0-6738-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 分析字符串 示例 请参见 发送反馈意见 本主题演示如何分析字符串,以便用 C# 或 Visual Basic 创建 XML 树。 示例 请参见 下面的 C# 代码演示如何分析字符串。 在 Visual Basic 中,可以以类似方式分析字符串。 但是,使用 XML 文本效率会更高(如下面的代码所示), 因为使用 XML 文本不会像从字符串分析 XML 那样要牺牲性能。 通过使用 XML 文本,可以将 XML 复制和粘贴到 Visual Basic 程序中。 C# 复制代码 XElement contacts = XElement.Parse( @" Patrick Hines 206-555-0144 425-555-0145
123 Main St Mercer Island WA 68042
10
Gretchen Rivas 206-555-0163
123 Main St Mercer Island WA 68042
11
"); Console.WriteLine(contacts); 注意: 分析文本或从文本文件加载 XML 文档比函数构造的效率更低。 如果要从代码初始化 XML 树,使用函数 构造比分析文本所占用的处理器时间更少。 概念 分析 XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/833642d6-e01a-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 从文件加载 XML 示例 请参见 发送反馈意见 本主题演示如何通过使用 XElement.Load 方法从 URI 加载 XML。 示例 请参见 下面的示例演示如何从文件加载 XML 文档。 下面的示例加载 books.xml 并将 XML 树输出到控制台。 本示例使用下面的 XML 文档:示例 XML 文件: 图书 (LINQ to XML)。 此代码生成以下输出: C# 复制代码 XElement booksFromFile = XElement.Load(@"books.xml"); Console.WriteLine(booksFromFile); Xml 复制代码 Garghentini, Davide XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML. Garcia, Debra Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world. 概念 分析 XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5b4865ef-4a52-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 加载或分析 XML 时保留空白 请参见 发送反馈意见 本主题说明如何控制 LINQ to XML 的空白行为。 一种常用方案是读取缩进的 XML,在内存中创建一个没有任何空白文本节点(即不保留空白)的 XML 树,对该 XML 执行某些操作,然后保存带缩进的 XML。 在序列化带格式的 XML 时,只保留 XML 树中有意义的空白。 这是 LINQ to XML 的默认行为。 另一个常见的方案是读取和修改已经有意缩进的 XML。 您可能不想以任何方式更改这种缩进。 在 LINQ to XML 中,如果在加载或解析 XML 时保留空白,并在序列化 XML 时禁用格式设置,就可以实现此目的。 本主题说明用于填充 XML 树的方法的空白行为。 有关序列化 XML 树时如何控制空白的信息,请参见序列化时保留 空白。 用于填充 XML 树的方法的行为 请参见 XElement 和 XDocument 类中的以下方法用于填充 XML 树。 可以从文件、TextReader、XmlReader 或字符 串填充 XML 树: z XElement.Load z XElement.Parse z XDocument.Load z XDocument.Parse 如果方法不采用 LoadOptions 作为参数,则该方法将不会保留无意义的空白。 在多数情况下,如果方法采用 LoadOptions 作为参数,则您可以选择保留无意义的空白作为 XML 树中的文本 节点。 但如果方法从 XmlReader 中加载 XML,则 XmlReader 将确定是否保留空白。 设置 PreserveWhitespace 不会有影响。 使用这些方法时,如果保留空白,则会将无意义的空白插入到 XML 树中作为 XText 节点。 如果不保留空 白,则不会插入文本节点。 您可以使用 XmlWriter 创建一个 XML 树。 写入到 XmlWriter 的节点会在树中进行填充。 但在使用此方法生 成 XML 树时,不管节点是否为空白或是否为无意义的空白,都将保留所有节点。 概念 分析 XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/093a7169-a04e-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 捕捉分析错误 示例 请参见 发送反馈意见 本主题演示如何检测格式不正确或无效的 XML。 LINQ to XML 是在 XmlReader 之上实现的。 如果将格式不正确或无效的 XML 传递给 LINQ to XML,则基础 XmlReader 类将 引发异常。 用于分析 XML 的各个方法(如 XElement.Parse)不会捕捉异常;发生异常时会冒泡,以便应用程序捕捉异常。 请注意,如果使用 XML 文本,则无法获取分析错误。 Visual Basic 编译器将捕捉格式不正确或无效的 XML 错误。 示例 请参见 下面的代码尝试分析无效的 XML: 运行此代码时,会引发以下异常: 有关 XElement.Parse、XDocument.Parse、XElement.Load 和 XDocument.Load 方法可能会引发的异常的更多信息,请参 见 XmlReader 文档。 C# 复制代码 try { XElement contacts = XElement.Parse( @" Jim Wilson "); Console.WriteLine(contacts); } catch (System.Xml.XmlException e) { Console.WriteLine(e.Message); } 复制代码 The 'Contacts' start tag on line 1 does not match the end tag of 'Contcts'. Line 5, position 13. 概念 分析 XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5eb05407-95f6-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 从 XmlReader 创建树 示例 请参见 发送反馈意见 本主题演示如何直接从 XmlReader 创建 XML 树。 若要从 XmlReader 创建 XElement,必须将 XmlReader 定位在 元素节点上。 XmlReader 将跳过注释和处理指令,但如果 XmlReader 定位在文本节点上,则将引发错误。 若要避 免这类错误,请在从 XmlReader 创建 XML 树之前,始终将 XmlReader 定位在元素上。 示例 请参见 本示例使用下面的 XML 文档:示例 XML 文件: 图书 (LINQ to XML)。 下面的代码创建一个 T:System.Xml.XmlReader 对象,然后读取节点,直到找到第一个元素节点。 然后加 载 XElement 对象。 本示例生成以下输出: C# 复制代码 XmlReader r = XmlReader.Create("books.xml"); while (r.NodeType != XmlNodeType.Element) r.Read(); XElement e = XElement.Load(r); Console.WriteLine(e); Xml 复制代码 Garghentini, Davide XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML. Garcia, Debra Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world. 概念 分析 XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/059694a1-1e04-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 流式处理 XmlReader 中的 XML 片段 示例 请参见 发送反馈意见 如果必须处理很大的 XML 文件,将整个 XML 树加载到内存可能不可行。 本主题演示如何使用 XmlReader 对片段 进行流式处理。 使用 XmlReader 读取 XElement 对象的一种最有效方式是编写您自己的自定义轴方法。 轴方法通常会返回一个集 合,比如 XElement 的 IEnumerable(T),如本主题中的示例所示。 在自定义轴方法中,在通过调用 ReadFrom 方 法创建 XML 片段后,可以使用 yield return 返回该集合。 这可为您的自定义轴方法提供延迟执行语义。 在从 XmlReader 对象创建 XML 树时,XmlReader 必须位于元素上。 ReadFrom 方法在读取该元素的结束标记之前 不会返回。 如果想要创建一个部分树,可实例化 XmlReader,将读取器定位在要转换为 XElement 树的节点上,然后创建 XElement 对象。 如何: 流处理可访问标头信息的 XML 片段主题包含有关如何流式处理更复杂文档的信息和示例。 如何: 执行大型 XML 文档的流式转换主题包含如何使用 LINQ to XML 在保持小内存需求量的同时转换极大 XML 文档的示例。 示例 本示例创建一个自定义轴方法。 可以通过使用 LINQ 查询来查询该方法。 自定义轴方法 StreamRootChildDoc 是一个专门设计的方法,用于读取具有重复 Child 元素的文档。 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 static IEnumerable StreamRootChildDoc(StringReader stringReader) { using (XmlReader reader = XmlReader.Create(stringReader)) { reader.MoveToContent(); // Parse the file and display each of the nodes. while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: if (reader.Name == "Child") { XElement el = XElement.ReadFrom(reader) as XElement; if (el != null) yield return el; } break; } } } } static void Main(string[] args) { string markup = @" aaa bbb ccc "; IEnumerable grandChildData = from el in StreamRootChildDoc(new StringReader(markup)) where (int)el.Attribute("Key") > 1 select (string)el.Element("GrandChild"); foreach (string str in grandChildData) { Console.WriteLine(str); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/424b53a5-3b97-4... 请参见 本示例生成以下输出: 在本示例中,源文档非常小。 但是即使有数百万个 Child 元素,本示例也仍具有很小的内存需求量。 } } 复制代码 bbb ccc 概念 分析 XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/424b53a5-3b97-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 用 XmlWriter 填充 XML 树 (LINQ to XML) 示例 请参见 发送反馈意见 填充 XML 树的一种方式是使用 CreateWriter 创建一个 XmlWriter,然后写入 XmlWriter。 XML 树将用写入到 XmlWriter 的所有节点进行填充。 在与预期会向 XmlWriter 写入数据的另一个类(如 XslCompiledTransform)一起使用 LINQ to XML 时,通常应使用此方 法。 示例 请参见 CreateWriter 可能在调用 XSLT 转换时使用。 本示例创建一个 XML 树,从该 XML 树创建一个 XmlReader,创建 一个新文档,然后创建要写入到新文档的 XmlWriter。 示例然后调用 XSLT 转换,传入 XmlReader 和 XmlWriter。 在转换成功完成后,将使用转换结果填充新的 XML 树。 本示例生成以下输出: C# 复制代码 string xslMarkup = @" "; XDocument xmlTree = new XDocument( new XElement("Parent", new XElement("Child1", "Child1 data"), new XElement("Child2", "Child2 data") ) ); XDocument newTree = new XDocument(); using (XmlWriter writer = newTree.CreateWriter()) { // Load the style sheet. XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load(XmlReader.Create(new StringReader(xslMarkup))); // Execute the transformation and output the results to a writer. xslt.Transform(xmlTree.CreateReader(), writer); } Console.WriteLine(newTree); Xml 复制代码 Child1 data Child2 data 概念 创建 XML 树 参考 CreateWriter XmlWriter XslCompiledTransform 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/15c56bab-04df-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用 XSD 进行验证 (LINQ to XML) 示例 请参见 发送反馈意见 System.Xml.Schema 命名空间包含扩展方法,这些扩展方法可以简化针对 XML 架构定义语言 (XSD) 文件验证 XML 树的过程。 有关更多信息,请参见 Validate 方法文档。 示例 下面的示例创建一个 XmlSchemaSet,然后针对架构集验证两个 XDocument 对象。 其中一个文档有效,另一个文档无效。 本示例生成以下输出: 下面的示例按照示例 XSD 文件: 客户和订单中的架构验证示例 XML 文件: 客户和订单 (LINQ to XML) 中的 XML 文档是 否有效。 然后修改源 XML 文档。 它更改第一个客户的 CustomerID 属性。 更改后,订单将指向不存在的客户,因此该 XML 文档不再有效。 本示例使用下面的 XML 文档: 本示例使用下面的 XSD 架构:示例 XSD 文件: 客户和订单。 C# 复制代码 string xsdMarkup = @" "; XmlSchemaSet schemas = new XmlSchemaSet(); schemas.Add("", XmlReader.Create(new StringReader(xsdMarkup))); XDocument doc1 = new XDocument( new XElement("Root", new XElement("Child1", "content1"), new XElement("Child2", "content1") ) ); XDocument doc2 = new XDocument( new XElement("Root", new XElement("Child1", "content1"), new XElement("Child3", "content1") ) ); Console.WriteLine("Validating doc1"); bool errors = false; doc1.Validate(schemas, (o, e) => { Console.WriteLine("{0}", e.Message); errors = true; }); Console.WriteLine("doc1 {0}", errors ? "did not validate" : "validated"); Console.WriteLine(); Console.WriteLine("Validating doc2"); errors = false; doc2.Validate(schemas, (o, e) => { Console.WriteLine("{0}", e.Message); errors = true; }); Console.WriteLine("doc2 {0}", errors ? "did not validate" : "validated"); 复制代码 Validating doc1 doc1 validated Validating doc2 The element 'Root' has invalid child element 'Child3'. List of possible elements expected: 'Child2'. doc2 did not validate C# 复制代码 XmlSchemaSet schemas = new XmlSchemaSet(); schemas.Add("", "CustomersOrders.xsd"); Console.WriteLine("Attempting to validate"); XDocument custOrdDoc = XDocument.Load("CustomersOrders.xml"); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/481a97fa-6e96-4... 请参见 本示例生成以下输出: bool errors = false; custOrdDoc.Validate(schemas, (o, e) => { Console.WriteLine("{0}", e.Message); errors = true; }); Console.WriteLine("custOrdDoc {0}", errors ? "did not validate" : "validated"); Console.WriteLine(); // modify the source document so that it will not validate. custOrdDoc.Root.Element("Orders").Element("Order").Element("CustomerID").Value = "AAAAA"; Console.WriteLine("Attempting to validate after modification"); errors = false; custOrdDoc.Validate(schemas, (o, e) => { Console.WriteLine("{0}", e.Message); errors = true; }); Console.WriteLine("custOrdDoc {0}", errors ? "did not validate" : "validated"); 复制代码 Attempting to validate custOrdDoc validated Attempting to validate after modification The key sequence 'AAAAA' in Keyref fails to refer to some key. custOrdDoc did not validate 概念 创建 XML 树 参考 Validate 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/481a97fa-6e96-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XElement 和 XDocument 对象的有效内容 请参见 发送反馈意见 本主题详细描述可以传递给构造函数的有效参数,以及用于向元素和文档添加内容的方法。 有效内容 文档的有效内容 允许添加内容的构造函数和函数 由于查询的计算结果通常是 XElement 的 IEnumerable(T) 或 XAttribute 的 IEnumerable(T),因而可以很方便 地将查询的结果作为内容,传递给用于填充 XML 树的方法和构造函数。 添加简单内容时,可以将多种类型传递给此方法。 有效类型包括: z String z Double z Single z Decimal z Boolean z DateTime z TimeSpan z DateTimeOffset z 实现 ToString() 的任何类型 z 实现 IEnumerable(T) 的任何类型 添加复杂内容时,可以将多种类型传递给此方法: z XObject z XNode z XAttribute z 实现 IEnumerable(T) 的任何类型 如果对象实现 IEnumerable(T),则枚举对象中的集合,并添加集合中的所有项。 如果集合包含 XNode 或 XAttribute 对象,则单独添加集合中的每一项。 如果集合包含文本(或转换成文本的对象),则集合中的文 本是串联在一起的,并作为单个文本节点添加。 如果内容为 null,则不添加任何内容。 传递集合时,允许集合中的项为 null。 集合中的 null 项对树没有 任何影响。 添加的属性必须在其包含元素内具有唯一名称。 在添加 XNode 或 XAttribute 对象时,如果新内容没有父级,则直接将这些对象附加到 XML 树中。 如果新内 容已经有父级,并且是另一 XML 树的一部分,则克隆新内容,并将新克隆的内容附加到 XML 树。 属性和简单内容无法添加到文档。 需要您创建 XDocument 的情况不是很多。 相反,您通常可以创建具有 XElement 根节点的 XML 树。 除非 对创建文档有特定要求(例如,因为必须在顶级创建处理指令和注释,或者必须支持文档类型),否则使用 XElement 作为根节点通常更为方便。 文档的有效内容包括: z 零个或一个 XDocumentType 对象。 文档类型必须在元素之前。 z 零个或一个元素。 z 零个或多个注释。 z 零个或多个处理指令。 z 零个或多个仅包含空白的文本节点。 下面的方法允许您将子内容添加到 XElement 或 XDocument 中: 方法 说明 XElement 构造一个 XElement。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/aee2d319-5c5f-4b... 请参见 XDocument 构造一个 XDocument。 Add 添加到 XElement 或 XDocument 的子内容的末尾。 AddAfterSelf 在 XNode 后面添加内容。 AddBeforeSelf 在 XNode 前面添加内容。 AddFirst 在 XContainer 的子内容的开头添加内容。 ReplaceAll 替换 XElement 的所有内容(子节点和属性)。 ReplaceAttributes 替换 XElement 的属性。 ReplaceNodes 用新内容替换子节点。 ReplaceWith 用新内容替换节点。 概念 创建 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/aee2d319-5c5f-4b... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用 XML 命名空间 请参见 发送反馈意见 本节中的主题描述 LINQ to XML 如何支持命名空间。 本节内容 请参见 主题 说明 命名空间概述 (LINQ to XML) 本主题介绍命名空间、XName 类和 XNamespace 类。 C# 中的命名空间 (LINQ to XML) 这一组主题说明在 C# 中如何使用命名空间。 Visual Basic 中的命名空间 (LINQ to XML) 这一组主题说明在 Visual Basic 中如何使用命名空间。 如何: 针对命名空间中的 XML 编写查询 演示在使用 Visual Basic 和 C# 语言时如何在 LINQ to XML 查询中指 定 XML 命名空间。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e3003209-3234-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 命名空间概述 (LINQ to XML) 请参见 发送反馈意见 本主题介绍命名空间、XName 类和 XNamespace 类。 XML 名称 请参见 XML 名称常常是导致 XML 编程复杂性的原因。 XML 名称由 XML 命名空间(也称为 XML 命名空间 URI)和 本地名称组成。 XML 命名空间的作用与基于 .NET Framework 的程序中命名空间的作用相似。 它能够唯一限 定元素和属性的名称。 这有助于避免 XML 文档各个部分之间的名称冲突。 声明 XML 命名空间后,可以选 择只需在此命名空间内唯一的本地名称。 XML 名称的另一方面是 XML 命名空间前缀。 XML 名称的复杂性大都是由 XML 前缀引起的。 这些前缀可用 来创建 XML 命名空间的快捷方式,从而使 XML 文档更加简洁易懂。 但是,XML 前缀仅在其上下文中才有意 义,这就增加了复杂性。 例如,XML 前缀 aw 可以与 XML 树的一部分中的一个 XML 命名空间关联,也可以 与该 XML 树的另一部分中的另一个 XML 命名空间关联。 通过 C# 使用 LINQ to XML 的一个优点是开发人员无需使用 XML 前缀,从而简化了 XML 名称。 当 LINQ to XML 加载或解析 XML 文档时,每个 XML 前缀都解析为它对应的 XML 命名空间。 之后,当您处理使用命名 空间的文档时,您几乎总是通过命名空间 URI,而不是通过命名空间前缀来访问命名空间。 开发人员在 LINQ to XML 中使用 XML 名称时,他们始终使用完全限定的 XML 名称(即,XML 命名空间和本地名称)。 但是,如有必要,LINQ to XML 允许您使用和控制命名空间前缀。 如果通过 Visual Basic 和 XML 文本使用 LINQ to XML,则在命名空间中处理文档时必须使用命名空间前缀。 在 LINQ to XML 中,表示 XML 名称的类为 XName。 XML 名称在整个 LINQ to XML API 中频繁出现,任何时 候只要需要 XML 名称,您就会发现 XName 参数。 但是,很少会直接使用 XName。 XName 包含一个从字 符串的隐式转换。 有关更多信息,请参见 XNamespace 和 XName。 概念 使用 XML 命名空间 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c06ec90e-a1f2-40... 全部折叠 代码:C# 语言集成查询 (LINQ) C# 中的命名空间 (LINQ to XML) 请参见 发送反馈意见 本节中的主题说明如何使用 C# 中的命名空间。 本节内容 请参见 主题 说明 如何: 创建包含命名空间的文档 (LINQ to XML) (C#) 演示如何创建包含命名空间的文档。 如何: 控制命名空间前缀 (C#) (LINQ to XML) 演示如何通过向 XML 树中插入命名空间属性来控制命名 空间前缀。 C# 中的默认命名空间的范围 演示为默认命名空间中的 XML 编写查询的适当方式。 概念 使用 XML 命名空间 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/62f60fea-6e98-42... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 创建包含命名空间的文档 (LINQ to XML) (C#) 示例 请参见 发送反馈意见 本主题演示如何创建包含命名空间的文档。 若要创建一个属于命名空间的元素或属性,可以使用包含 XNamespace 的 XName(包含命名空间所需的 URI)进 行创建。 每个元素和属性都包含一个 XName,而每个 XName 都包含一个 XNamespace。 即使某个元素不在命名 空间中,该元素的 XName 仍包含命名空间 XNamespace.None。 XName.Namespace 属性一定不会是 null。 您可以选择在 XML 树中创建命名空间属性。 这些属性声明命名空间。 命名空间属性的创建包含如何控制命名空间 前缀,以及命名空间是否为默认命名空间。 若要创建一个声明默认命名空间的属性,请创建一个名称为“xmlns”的属性,而无需命名空间。 该属性的值即是默 认命名空间 URI。 若要创建一个声明具有前缀的命名空间的属性,请创建一个属性,该属性的名称的命名空间为 Xmlns,该属性的名 称为命名空间前缀。 该属性的值即是命名空间的 URI。 如果 XML 树包含命名空间中的元素或属性,并且没有任何属性声明命名空间,则在序列化过程中,LINQ to XML 确 定是创建默认命名空间,还是创建非默认命名空间并向它们分配前缀。 XNamespace 对象一定不会是原子化的;即,如果两个 XNamespace 对象具有完全相同的 URI,则它们将共享同一 实例。 示例 下面的示例创建一个包含一个命名空间的文档。 由于本示例中的文档仅包含一个命名空间,而且没有任何命 名空间属性声明命名空间,因此,LINQ to XML 会创建一个默认命名空间。 本示例生成以下输出: 下面的示例创建一个包含一个命名空间的文档。 另外,还创建一个属性,该属性声明具有命名空间前缀的命 名空间。 若要创建一个声明具有前缀的命名空间的属性,请创建一个属性,该属性的名称的命名空间为 Xmlns,该属性的名称为命名空间前缀。 该属性的值即是命名空间的 URI。 本示例生成以下输出: 下面的示例演示如何创建一个包含两个命名空间的文档。 一个是默认命名空间。 另一个是具有前缀的命名空 间。 通过在根元素中包括命名空间属性,命名空间进行了序列化,从而 http://www.adventure-works.com 是默 认命名空间,而 www.fourthcoffee.com 用 fc 前缀进行了序列化。 C# 复制代码 // Create an XML tree in a namespace. XNamespace aw = "http://www.adventure-works.com"; XElement root = new XElement(aw + "Root", new XElement(aw + "Child", "child content") ); Console.WriteLine(root); Xml 复制代码 child content C# 复制代码 // Create an XML tree in a namespace, with a specified prefix XNamespace aw = "http://www.adventure-works.com"; XElement root = new XElement(aw + "Root", new XAttribute(XNamespace.Xmlns + "aw", "http://www.adventure-works.com"), new XElement(aw + "Child", "child content") ); Console.WriteLine(root); Xml 复制代码 child content C# 复制代码 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/af4a595e-ffb2-41... 请参见 本示例生成以下输出: 下面的示例演示如何创建一个包含两个命名空间的文档,这两个命名空间都具有命名空间前缀。 本示例生成以下输出: 另一种可获得相同结果的方法是使用扩展名,而不是声明和创建一个 XNamespace 对象。 这种方法的性能较低。 每次将包含扩展名的字符串传递给 LINQ to XML 时,都必须分析名称,查找原子化命 名空间,再查找原子化名称。 这个过程会占用 CPU 时间。 如果性能很重要,您可能希望使用显式声明和使 用 XNamespace 对象的方法。 本示例生成以下输出: // The http://www.adventure-works.com namespace is forced to be the default namespace. XNamespace aw = "http://www.adventure-works.com"; XNamespace fc = "www.fourthcoffee.com"; XElement root = new XElement(aw + "Root", new XAttribute("xmlns", "http://www.adventure-works.com"), new XAttribute(XNamespace.Xmlns + "fc", "www.fourthcoffee.com"), new XElement(fc + "Child", new XElement(aw + "DifferentChild", "other content") ), new XElement(aw + "Child2", "c2 content"), new XElement(fc + "Child3", "c3 content") ); Console.WriteLine(root); Xml 复制代码 other content c2 content c3 content C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XNamespace fc = "www.fourthcoffee.com"; XElement root = new XElement(aw + "Root", new XAttribute(XNamespace.Xmlns + "aw", aw.NamespaceName), new XAttribute(XNamespace.Xmlns + "fc", fc.NamespaceName), new XElement(fc + "Child", new XElement(aw + "DifferentChild", "other content") ), new XElement(aw + "Child2", "c2 content"), new XElement(fc + "Child3", "c3 content") ); Console.WriteLine(root); Xml 复制代码 other content c2 content c3 content C# 复制代码 // Create an XML tree in a namespace, with a specified prefix XElement root = new XElement("{http://www.adventure-works.com}Root", new XAttribute(XNamespace.Xmlns + "aw", "http://www.adventure-works.com"), new XElement("{http://www.adventure-works.com}Child", "child content") ); Console.WriteLine(root); Xml 复制代码 child content 概念 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/af4a595e-ffb2-41... C# 中的命名空间 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/af4a595e-ffb2-41... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 控制命名空间前缀 (C#) (LINQ to XML) 示例 请参见 发送反馈意见 本主题介绍在序列化 XML 树时如何控制命名空间前缀。 在很多情况下,不需要控制命名空间前缀。 需要控制命名空间前缀的最常见原因是:您使用的是要求对命名空间前缀进行特定控制的其他系统或 XML 编程工 具。 例如,您可能正在操作 XSLT 样式表或 XAML 文档,其中包含引用特定命名空间前缀的嵌入式 XPath 表达 式,因此一定要使用这些特定前缀对文档进行序列化。 需要控制命名空间前缀的另一个常见原因是:您希望用户手动编辑 XML 文档,而且您希望创建方便用户键入的命名 空间前缀。 例如,您可能正在生成 XSD 文档。 架构约定建议您使用 xs 或 xsd 作为架构命名空间的前缀。 若要控制命名空间前缀,请插入声明命名空间的属性。 如果使用特定前缀声明命名空间,LINQ to XML 将在序列化 时尝试接受此命名空间前缀。 若要创建一个声明具有前缀的命名空间的属性,请创建一个属性,该属性的名称的命名空间为 Xmlns,该属性的名 称为命名空间前缀。 该属性的值即是命名空间的 URI。 示例 请参见 本示例声明两个命名空间。 它指定 http://www.adventure-works.com 命名空间具有 aw 前缀, www.fourthcoffee.com 命名空间具有 fc 前缀。 本示例生成以下输出: C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XNamespace fc = "www.fourthcoffee.com"; XElement root = new XElement(aw + "Root", new XAttribute(XNamespace.Xmlns + "aw", "http://www.adventure-works.com"), new XAttribute(XNamespace.Xmlns + "fc", "www.fourthcoffee.com"), new XElement(fc + "Child", new XElement(aw + "DifferentChild", "other content") ), new XElement(aw + "Child2", "c2 content"), new XElement(fc + "Child3", "c3 content") ); Console.WriteLine(root); Xml 复制代码 other content c2 content c3 content 概念 C# 中的命名空间 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/a40d4479-f1b9-4... 全部折叠 代码:C# 语言集成查询 (LINQ) C# 中的默认命名空间的范围 请参见 发送反馈意见 XML 树中表示的默认命名空间不在查询范围内。 如果您的 XML 在默认命名空间内,仍须声明一个 XNamespace 变 量,并将该变量与本地名称组合在一起,生成一个限定名,在查询中使用。 查询 XML 树时遇到的一个最常见问题是,如果 XML 树具有默认命名空间,开发人员在编写查询时,有时会将 XML 视为不在命名空间内。 本主题的第一个示例集演示一种加载并按不正确方式查询默认命名空间中的 XML 的典型方式。 第二个示例集演示必需的更正,以便可以查询命名空间中的 XML。 有关更多信息,请参见使用 XML 命名空间。 示例 示例 此示例演示如何在命名空间中创建 XML 和一个返回空结果集的查询。 代码 注释 此示例产生下面的结果: C# 复制代码 XElement root = XElement.Parse( @" 1 2 3 4 5 6 "); IEnumerable c1 = from el in root.Elements("Child") select el; Console.WriteLine("Result set follows:"); foreach (XElement el in c1) Console.WriteLine((int)el); Console.WriteLine("End of result set"); 复制代码 Result set follows: End of result set 本示例演示如何在命名空间中创建 XML 和一个正确编码的查询。 在使用 C# 时,解决方法是声明和初始化一个 XNamespace 对象,并在指定 XName 对象时使用它。 在这种 情况下,Elements 方法的参数是一个 XName 对象。 而在使用 Visual Basic 时,解决方法是声明和初始化一个全局默认命名空间。 这样会将所有 XML 属性放入该 默认命名空间。 无需对该示例做任何其他修改,即可使它正常运行。 代码 C# 复制代码 XElement root = XElement.Parse( @" 1 2 3 4 5 6 "); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/91498bb4-9c36-4... 请参见 注释 此示例产生下面的结果: XNamespace aw = "http://www.adventure-works.com"; IEnumerable c1 = from el in root.Elements(aw + "Child") select el; Console.WriteLine("Result set follows:"); foreach (XElement el in c1) Console.WriteLine((int)el); Console.WriteLine("End of result set"); 复制代码 Result set follows: 1 2 3 End of result set 概念 C# 中的命名空间 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/91498bb4-9c36-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 针对命名空间中的 XML 编写查询 示例 请参见 发送反馈意见 若要针对命名空间中的 XML 编写查询,必须在查询中使用具有正确命名空间的 XName 对象。 对于 C#,最常用的方法是使用包含 URI 的字符串初始化 XNamespace,然后使用加法运算符重载来组合命名空间 和本地名称。 使用 Visual Basic 时,最常用的方法是定义一个全局命名空间,然后使用那些使用该全局命名空间的 XML 文本和 XML 属性。 您可以定义一个全局默认命名空间,在这种情况中,XML 文本中的元素将默认位于该命名空间中。 或 者,您可以定义一个具有前缀的全局命名空间,然后根据需要在 XML 文本和 XML 属性中使用该前缀。 与其他形式 的 XML 一样,默认情况下,属性始终不在任何命名空间中。 示例 下面的示例创建一个位于默认命名空间中的 XML 树。 然后检索元素的集合。 本示例生成以下输出: 如果您用 C# 对使用带前缀的命名空间的 XML 树编写查询,则在编写查询时所使用的方法与在查询位于默认 命名空间中的 XML 时所使用的方法完全相同。 虽然可以在序列化时控制前缀,可以在给定前缀的情况下确定 命名空间 URI,并且可以确定命名空间的前缀,但在很大程度上,LINQ to XML 编程接口中已不再强调命名空 间前缀。 但在使用 XML 文本和在 Visual Basic 中对使用带前缀的命名空间的 XML 树编写查询时,所使用的方法不同。 您通常要使用 Imports 语句导入带前缀的命名空间。 然后在元素和属性名称中使用该前缀,并在使用可访问 XML 树的 XML 属性时也要使用该前缀。 下面的示例创建一个位于具有前缀的默认命名空间中的 XML 树。 然后检索元素的集合。 本示例生成以下输出: C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XElement root = XElement.Parse( @" 1 2 3 4 5 6 "); IEnumerable c1 = from el in root.Elements(aw + "Child") select el; foreach (XElement el in c1) Console.WriteLine((int)el); 复制代码 1 2 3 C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XElement root = XElement.Parse( @" 1 2 3 4 5 6 "); IEnumerable c1 = from el in root.Elements(aw + "Child") select el; foreach (XElement el in c1) Console.WriteLine((int)el); 复制代码 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6beb720e-ad90-4... 请参见 1 2 3 概念 使用 XML 命名空间 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6beb720e-ad90-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 序列化 XML 树 请参见 发送反馈意见 序列化 XML 树意味着从 XML 树生成 XML。 可以将 XML 树序列化到文件、TextWriter 类的具体实现或 XmlWriter 的具体实现。 可以控制序列化的各个方面。 例如,可以控制是否缩进序列化的 XML,以及是否写入 XML 声明。 本节内容 请参见 主题 说明 序列化时保留空白 介绍如何在序列化 XML 树时控制空白行为。 带 XML 声明的序列化 介绍如何序列化包含 XML 声明的 XML 树。 序列化为 File、TextWriter 和 XmlWriter 描述如何将文档序列化到 File、TextWriter 或 XmlWriter。 序列化为 XmlReader(调用 XSLT) 描述如何创建一个 XmlReader,使另一个模块能够通过它读取 XML 树的内容。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f01bdf96-c5a9-48... 全部折叠 代码:C# 语言集成查询 (LINQ) 序列化时保留空白 请参见 发送反馈意见 本主题描述在序列化 XML 树时如何控制空白。 一种常用情况是读取缩进的 XML,在内存中创建一个没有任何空白文本节点(即不保留空白)的 XML 树,对该 XML 执行某些操作,然后保存带缩进的 XML。 在序列化带格式的 XML 时,只保留 XML 树中有意义的空白。 这是 LINQ to XML 的默认行为。 另一个常见的情况是读取和修改已经有意缩进的 XML。 您可能不想以任何方式更改这种缩进。 在 LINQ to XML 中,如果在加载或解析 XML 时保留空白,并在序列化 XML 时禁用格式设置,就可以实现此目的。 用于序列化 XML 树的方法的空白行为 请参见 XElement 和 XDocument 类中的以下方法用于序列化 XML 树。 可以将 XML 树序列化为文件、TextReader 或 XmlReader。 ToString 方法序列化为字符串。 z XElement.Save z XDocument.Save z XElement.ToString z XDocument.ToString 如果方法不接受 SaveOptions 作为参数,那么该方法将格式化(缩进)序列化的 XML。 在这种情况下,将丢 弃 XML 树中所有无意义的空白。 如果方法接受 SaveOptions 作为参数,那么该方法可以选择不格式化(缩进)序列化的 XML。 在这种情况 下,将保留 XML 树中的所有空白。 概念 序列化 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fb146217-0a49-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 带 XML 声明的序列化 请参见 发送反馈意见 本主题说明如何控制序列化是否生成 XML 声明。 请注意,序列化为 File 或 TextWriter 会生成 XML 声明。 在序列化为 XmlWriter 时,编写器设置(在 XmlWriterSettings 对象中指定)将确定是否生成 XML 声明。 XML 声明的生成 请参见 如果要序列化为 XmlWriter 以外的对象,则下面的方法会生成一个 XML 声明: z XElement.Save z XDocument.Save 如果要使用 ToString 方法序列化为字符串,则生成的 XML 不会包括 XML 声明。 带 XML 声明的序列化 下面的示例创建一个 XElement,将文档保存到文件,然后将文件打印到控制台: 本示例生成以下输出: 不带 XML 声明的序列化 下面的示例演示如何将一个 XElement 保存到一个 XmlWriter。 本示例生成以下输出: C# 复制代码 XElement root = new XElement("Root", new XElement("Child", "child content") ); root.Save("Root.xml"); string str = File.ReadAllText("Root.xml"); Console.WriteLine(str); Xml 复制代码 child content C# 复制代码 StringBuilder sb = new StringBuilder(); XmlWriterSettings xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; using (XmlWriter xw = XmlWriter.Create(sb, xws)) { XElement root = new XElement("Root", new XElement("Child", "child content") ); root.Save(xw); } Console.WriteLine(sb.ToString()); Xml 复制代码 child content 概念 序列化 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/30f1b564-62b5-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 序列化为 File、TextWriter 和 XmlWriter 请参见 发送反馈意见 可以将 XML 树序列化为 File、TextWriter 或 XmlWriter。 可以使用 ToString 方法,将任何 XML 组件(包括 XDocument 和 XElement)序列化为一个字符串。 在序列化为字符串时,如果要禁止格式化,可以使用 XNode.ToString 方法。 序列化为 URI、TextWriter 或 XmlWriter 请参见 序列化为文件时,默认行为是格式化(缩进)生成的 XML 文档。 缩进时,不会保留 XML 树中无意义的空 白。 若要使用格式化方式进行序列化,请使用不将 SaveOptions 作为参数的以下方法的重载之一: z XDocument.Save z XElement.Save 如果要选择不在 XML 树中缩进,并保留无意义空白,请使用将 SaveOptions 作为参数的以下方法的重载之 一: z XDocument.Save z XElement.Save 有关示例,请参见相应的参考主题。 概念 序列化 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b66c36ea-e3e3-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 序列化为 XmlReader(调用 XSLT) 请参见 发送反馈意见 在使用 LINQ to XML 的 System.Xml 互操作性功能时,可以使用 CreateReader 来创建 XmlReader。 从创建的 XmlReader 进行读取的模块从 XML 树读取节点,并对它们进行相应的处理。 调用 XSLT 转换 请参见 此方法可能在调用 XSLT 转换时使用。 可以创建 XML 树,从 XML 树创建 XmlReader,创建新文档,然后创 建 XmlWriter 以写入新文档。 接着,可以调用 XSLT 转换,传入 XmlReader 和 XmlWriter。 在转换成功完 成后,使用转换的结果,填充新的 XML 树。 此示例产生以下输出: C# 复制代码 string xslMarkup = @" "; XDocument xmlTree = new XDocument( new XElement("Parent", new XElement("Child1", "Child1 data"), new XElement("Child2", "Child2 data") ) ); XDocument newTree = new XDocument(); using (XmlWriter writer = newTree.CreateWriter()) { // Load the style sheet. XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load(XmlReader.Create(new StringReader(xslMarkup))); // Execute the transformation and output the results to a writer. xslt.Transform(xmlTree.CreateReader(), writer); } Console.WriteLine(newTree); Xml 复制代码 Child1 data Child2 data 概念 序列化 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/adbba67a-69a7-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 轴 请参见 发送反馈意见 创建 XML 树或将 XML 文档加载到 XML 树之后,可以进行查询,从而查找元素和属性并检索它们的值。 在编写查询之前,必须了解 LINQ to XML 轴。 有两类轴方法: 第一类,是在单个 XElement 对象、XDocument 对 象或 XNode 对象上调用的方法。 这些方法对单个对象操作,返回 XElement、XAttribute 或 XNode 对象的集合。 第二类,是对集合操作并返回集合的扩展方法。 这些扩展方法可以:枚举源集合,在集合的每一项上调用适当的轴 方法,将结果串联起来。 本节内容 请参见 主题 说明 LINQ to XML 轴概述 介绍轴的定义,并说明如何在 LINQ to XML 查询的上下文中 使用轴。 如何: 检索元素集合 (LINQ to XML) 介绍 Elements 方法。 此方法检索元素的子元素集合。 如何: 检索元素的值 (LINQ to XML) 演示如何获取元素的值。 如何: 根据元素名称进行筛选 (LINQ to XML) 演示如何在使用轴时筛选元素名称。 如何: 调用链接轴方法 (LINQ to XML) 演示如何将调用链接到轴方法。 如何: 检索单个子元素 (LINQ to XML) 演示在给定子元素标记名的情况下,如何检索元素的单个子 元素。 如何: 检索属性集合 (LINQ to XML) 介绍 Attributes 方法。 此方法检索元素的属性。 如何: 检索单个属性 (LINQ to XML) 演示在给定属性名称的情况下,如何检索元素的单个属性。 如何: 检索属性的值 (LINQ to XML) 演示如何获取属性的值。 Visual Basic 中的语言集成轴 (LINQ to XML) 总结 Visual Basic 集成轴。 概念 Extension Methods (C# Programming Guide) 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/86ff2c9f-2ea1-43... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 轴概述 请参见 发送反馈意见 创建 XML 树或将 XML 文档加载到 XML 树之后,可以进行查询,从而查找元素和属性并检索它们的值。 您通过轴 方法来检索集合。 一些轴就是 XElement 和 XDocument 类中返回 IEnumerable(T) 集合的方法。 另一些轴方法是 Extensions 类中的扩展方法。 实现为扩展方法的轴对集合进行操作,然后返回集合。 如同 XElement 类概述中所述,XElement 对象表示单个元素节点。 元素的内容可以是复杂的(有时称为结构化内 容),也可以是简单元素。 简单元素可以是空的,也可以包含值。 如果节点包含结构化内容,则可以使用各种轴 方法来检索子代元素的枚举。 最常使用的轴方法是 Elements 和 Descendants。 除了返回集合的轴方法之外,还有两个方法会在 LINQ to XML 查询中经常用到。 Element 方法返回单个 XElement。 Attribute 方法返回单个 XAttribute。 对于很多应用来说,LINQ 查询提供了检查树、从树中提取数据以及转换树的最有效的方法。LINQ 查询对实现 IEnumerable(T) 的对象进行操作,LINQ to XML 轴返回 XElement 的 IEnumerable(T) 以及 XAttribute 的 IEnumerable(T) 集合。 需要使用这些集合来执行查询。 除了检索元素和属性集合的轴方法之外,还有一些轴方法可以十分详尽地循环访问树。 例如,可以处理树的节点, 而不是处理元素和属性。 节点比元素和属性有更细的粒度。 处理节点时,可以检查 XML 注释、文本节点、处理指 令以及其他方面。 该功能很重要,例如对正在编写字处理器并希望将文档保存为 XML 的用户非常有用。 但是,大 部分 XML 程序员主要关心的是元素、属性和它们的值。 用于检索元素集合的方法 用于检索单个元素的方法 用于检索属性集合的方法 下面是 XElement 类(或其基类)的方法汇总,您可以对 XElement 调用这些方法以返回元素集合。 方法 说明 XNode.Ancestors 返回此元素的上级的 XElement 的 IEnumerable(T)。 重载方法返回上 级的 XElement 的 IEnumerable(T),这些上级具有指定的 XName。 XContainer.Descendants 返回此元素的子代的 XElement 的 IEnumerable(T)。 重载方法返回子 代的 XElement 的 IEnumerable(T),这些子代具有指定的 XName。 XContainer.Elements 返回此元素的子元素的 XElement 的 IEnumerable(T)。 重载方法返回 子元素的 XElement 的 IEnumerable(T),这些子元素具有指定的 XName。 XNode.ElementsAfterSelf 返回此元素之后的元素的 XElement 的 IEnumerable(T)。 重载方法返 回此元素之后的元素的 XElement 的 IEnumerable(T),此元素之后的这 些元素具有指定的 XName。 XNode.ElementsBeforeSelf 返回此元素之前的元素的 XElement 的 IEnumerable(T)。 重载方法返 回此元素之前的元素的 XElement 的 IEnumerable(T),此元素之前的这 些元素具有指定的 XName。 XElement.AncestorsAndSelf 返回此元素及其上级的 XElement 的 IEnumerable(T)。 重载方法返回 元素的 XElement 的 IEnumerable(T),这些元素具有指定的 XName。 XElement.DescendantsAndSelf 返回此元素及其子代的 XElement 的 IEnumerable(T)。 重载方法返回 元素的 XElement 的 IEnumerable(T),这些元素具有指定的 XName。 下面的方法从 XElement 对象中检索单个子级。 方法 说明 XContainer.Element 返回具有指定 XName 的第一个子 XElement 对象。 下面的方法从 XElement 对象中检索属性。 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f25da2f0-6c42-4a... 用于检索单个属性的方法 请参见 方法 说明 XElement.Attributes 返回所有属性的 XAttribute 的 IEnumerable(T)。 下面的方法从 XElement 对象中检索单个属性。 方法 说明 XElement.Attribute 返回具有指定 XName 的 XAttribute。 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f25da2f0-6c42-4a... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 检索元素集合 (LINQ to XML) 示例 请参见 发送反馈意见 本主题演示 Elements 方法。 此方法检索元素的子元素集合。 示例 请参见 本示例循环访问 purchaseOrder 元素的子元素。 本示例使用下面的 XML 文档:示例 XML 文件: 典型采购订单 (LINQ to XML)。 本示例生成以下输出。 C# 复制代码 XElement po = XElement.Load("PurchaseOrder.xml"); IEnumerable childElements = from el in po.Elements() select el; foreach (XElement el in childElements) Console.WriteLine("Name: " + el.Name); 复制代码 Name: Address Name: Address Name: DeliveryNotes Name: Items 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/379d5bc0-7d6d-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 检索元素的值 (LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何获取元素的值。 有两种主要方法可以完成此操作。 一种方法是将 XElement 或 XAttribute 强制转 换为所需的类型。 然后,显式转换运算符将元素或属性的内容转换为指定的类型,并将其分配给变量。 此外,还 可以使用 XElement.Value 属性或 XAttribute.Value 属性。 但是,对于 C#,强制转换通常是更好的方法。 在检索可能存在也可能不存在的元素(或属性)的值时,如果将元 素或属性强制转换为可以为 null 的类型,则代码会更易于编写。 本主题最后一个示例对此进行了演示。 但是,无 法通过强制转换设置元素的内容,而通过 XElement.Value 属性可以做到这一点。 对于 Visual Basic,最好的方法是使用 XElement.Value 属性。 示例 若要检索元素的值,只需将 XElement 对象强制转换为所需的类型即可。 任何时候都可以将元素强制转换为 字符串,如下所示: 本示例生成以下输出: 此外,还可以将元素强制转换为字符串以外的其他类型。 例如,如果有一个包含一个整数的元素,可以将它 强制转换为 int,如下面的代码所示: 本示例生成以下输出: LINQ to XML 提供了以下数据类型的显式强制转换运算符:string、bool、bool?、int、int?、uint、uint?、 long、long?、ulong、ulong?、float、float?、double、double?、decimal、decimal?、DateTime、 DateTime?、TimeSpan、TimeSpan?、GUID 和 GUID?。 LINQ to XML 为 XAttribute 对象提供了相同的强制转换运算符。 可以使用 Value 属性来检索元素的内容: 本示例生成以下输出: 有时,尽管不能确定某个元素是否存在,还是会尝试检索该元素的值。 在这种情况下,将强制转换后的元素 分配给可以为 null 的类型(string 或 .NET Framework 中可以为 null 的类型之一)时,如果该元素不存在, 则将分配的变量设置为 null(Visual Basic 中的 Nothing)。 下面的代码演示当元素可能存在也可能不存在 时,使用强制转换比使用 Value 属性更加简单。 C# 复制代码 XElement e = new XElement("StringElement", "abcde"); Console.WriteLine(e); Console.WriteLine("Value of e:" + (string)e); 复制代码 abcde Value of e:abcde C# 复制代码 XElement e = new XElement("Age", "44"); Console.WriteLine(e); Console.WriteLine("Value of e:" + (int)e); 复制代码 44 Value of e:44 C# 复制代码 XElement e = new XElement("StringElement", "abcde"); Console.WriteLine(e); Console.WriteLine("Value of e:" + e.Value); 复制代码 abcde Value of e:abcde C# 复制代码 XElement root = new XElement("Root", 页码,1/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7bf496e9-b5a5-4... 请参见 此代码生成以下输出: 通常情况下,当使用强制转换来检索元素和属性的内容时,可以编写更简易的代码。 new XElement("Child1", "child 1 content"), new XElement("Child2", "2") ); // The following assignments show why it is easier to use // casting when the element might or might not exist. string c1 = (string)root.Element("Child1"); Console.WriteLine("c1:{0}", c1 == null ? "element does not exist" : c1); int? c2 = (int?)root.Element("Child2"); Console.WriteLine("c2:{0}", c2 == null ? "element does not exist" : c2.ToString()); string c3 = (string)root.Element("Child3"); Console.WriteLine("c3:{0}", c3 == null ? "element does not exist" : c3); int? c4 = (int?)root.Element("Child4"); Console.WriteLine("c4:{0}", c4 == null ? "element does not exist" : c4.ToString()); Console.WriteLine(); // The following assignments show the required code when using // the Value property when the element might or might not exist. // Notice that this is more difficult than the casting approach. XElement e1 = root.Element("Child1"); string v1; if (e1 == null) v1 = null; else v1 = e1.Value; Console.WriteLine("v1:{0}", v1 == null ? "element does not exist" : v1); XElement e2 = root.Element("Child2"); int? v2; if (e2 == null) v2 = null; else v2 = Int32.Parse(e2.Value); Console.WriteLine("v2:{0}", v2 == null ? "element does not exist" : v2.ToString()); XElement e3 = root.Element("Child3"); string v3; if (e3 == null) v3 = null; else v3 = e3.Value; Console.WriteLine("v3:{0}", v3 == null ? "element does not exist" : v3); XElement e4 = root.Element("Child4"); int? v4; if (e4 == null) v4 = null; else v4 = Int32.Parse(e4.Value); Console.WriteLine("v4:{0}", v4 == null ? "element does not exist" : v4.ToString()); 复制代码 c1:child 1 content c2:2 c3:element does not exist c4:element does not exist v1:child 1 content v2:2 v3:element does not exist v4:element does not exist 概念 LINQ to XML 轴 页码,2/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7bf496e9-b5a5-4... 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7bf496e9-b5a5-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 根据元素名称进行筛选 (LINQ to XML) 示例 请参见 发送反馈意见 当调用返回 XElement 的 IEnumerable(T) 的方法之一时,可以根据元素名称进行筛选。 示例 请参见 本示例演示如何检索子代的集合,但子代经过了筛选,因此集合仅包含具有指定名称的子代。 本示例使用下面的 XML 文档:示例 XML 文件: 典型采购订单 (LINQ to XML)。 此代码生成以下输出: 其他返回 XElement 集合的 IEnumerable(T) 的方法都遵循相同的模式。 它们的签名类似于 Elements 和 Descendants。 以下是具有相似方法签名的完整方法列表: z Ancestors z Descendants z Elements z ElementsAfterSelf z ElementsBeforeSelf z AncestorsAndSelf z DescendantsAndSelf 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的典型采购单。 此代码生成以下输出: C# 复制代码 XElement po = XElement.Load("PurchaseOrder.xml"); IEnumerable items = from el in po.Descendants("ProductName") select el; foreach(XElement prdName in items) Console.WriteLine(prdName.Name + ":" + (string) prdName); 复制代码 ProductName:Lawnmower ProductName:Baby Monitor C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XElement po = XElement.Load("PurchaseOrderInNamespace.xml"); IEnumerable items = from el in po.Descendants(aw + "ProductName") select el; foreach (XElement prdName in items) Console.WriteLine(prdName.Name + ":" + (string)prdName); 复制代码 {http://www.adventure-works.com}ProductName:Lawnmower {http://www.adventure-works.com}ProductName:Baby Monitor 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/a61e6608-ac26-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 调用链接轴方法 (LINQ to XML) 示例 请参见 发送反馈意见 一个常用方法是调用轴方法,然后调用一个扩展方法轴。 有两个返回元素集合、名称为 Elements 的轴:XContainer.Elements 方法和 Extensions.Elements 方法。 可以合并 这两个轴,在树的给定深度,查找具有指定名称的所有元素。 示例 本示例使用上文所述的两个轴,来查找所有 PurchaseOrder 元素中的所有 Address 元素中的所有 Name 元 素。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 本示例生成以下输出: 此方法有效是因为其中有一个 Elements 轴实现充当 XContainer 的 IEnumerable(T) 的扩展方法。 XElement 是从 XContainer 派生的,因此可以对调用 XContainer.Elements 方法的结果调用 Extensions.Elements 方法。 有时,当可能存在或不存在间隔上级时,您希望在特定的元素深度,检索所有的元素。 例如,在下面的文档 中,您可能要检索属于 Customer 元素的子元素的所有 ConfigParameter 元素,而不是属于 Root 元素的子 元素的 ConfigParameter。 若要执行此操作,您可以使用 Extensions.Elements 轴,如下所示: C# 复制代码 XElement purchaseOrders = XElement.Load("PurchaseOrders.xml"); IEnumerable names = from el in purchaseOrders .Elements("PurchaseOrder") .Elements("Address") .Elements("Name") select el; foreach (XElement e in names) Console.WriteLine(e); 复制代码 Ellen Adams Tai Yee Cristian Osorio Cristian Osorio Jessica Arnold Jessica Arnold Xml 复制代码 RootConfigParameter Frank FirstConfigParameter Bob Bill SecondConfigParameter C# 复制代码 XElement root = XElement.Load("Irregular.xml"); IEnumerable configParameters = root.Elements("Customer").Elements("Config"). Elements("ConfigParameter"); foreach (XElement cp in configParameters) 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b8e4d9a9-a87b-4... 请参见 本示例生成以下输出: 下面的示例演示针对命名空间中的 XML 的相同技术。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的多个采购单。 本示例生成以下输出: Console.WriteLine(cp); 复制代码 FirstConfigParameter SecondConfigParameter C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XElement purchaseOrders = XElement.Load("PurchaseOrdersInNamespace.xml"); IEnumerable names = from el in purchaseOrders .Elements(aw + "PurchaseOrder") .Elements(aw + "Address") .Elements(aw + "Name") select el; foreach (XElement e in names) Console.WriteLine(e); 复制代码 Ellen Adams Tai Yee Cristian Osorio Cristian Osorio Jessica Arnold Jessica Arnold 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b8e4d9a9-a87b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 检索单个子元素 (LINQ to XML) 示例 请参见 发送反馈意见 本主题说明如何在给定子元素名称的情况下检索单个子元素。 如果知道子元素的名称并且只有一个元素具有此名称,则只检索一个元素而不是一个集合会很 方便。 Element 方法返回具有指定 XName 的第一个子 XElement。 如果想要在 Visual Basic 中检索单个子元素,常用的方法是使用 XML 属性,然后使用数组索引器表示法检索第一个元素。 示例 请参见 下面的示例演示 Element 方法的用法。 本示例采用名为 po 的 XML 树并查找名为 Comment 的第一个元素。 Visual Basic 示例演示如何使用数组索引器表示法来检索单个元素。 本示例使用下面的 XML 文档:示例 XML 文件: 典型采购订单 (LINQ to XML)。 本示例生成以下输出: 下面的示例演示如何对命名空间中的 XML 使用相同的代码。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的典型采购单。 本示例生成以下输出: C# 复制代码 XElement po = XElement.Load("PurchaseOrder.xml"); XElement e = po.Element("DeliveryNotes"); Console.WriteLine(e); Xml 复制代码 Please leave packages in shed by driveway. C# 复制代码 XElement po = XElement.Load("PurchaseOrderInNamespace.xml"); XNamespace aw = "http://www.adventure-works.com"; XElement e = po.Element(aw + "DeliveryNotes"); Console.WriteLine(e); Xml 复制代码 Please leave packages in shed by driveway. 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0457856b-a93e-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 检索属性集合 (LINQ to XML) 示例 请参见 发送反馈意见 本主题介绍 Attributes 方法。 此方法检索元素的属性。 示例 请参见 下面的示例演示如何循环访问一个元素的属性集合。 此代码生成以下输出: C# 复制代码 XElement val = new XElement("Value", new XAttribute("ID", "1243"), new XAttribute("Type", "int"), new XAttribute("ConvertableTo", "double"), "100"); IEnumerable listOfAttributes = from att in val.Attributes() select att; foreach (XAttribute a in listOfAttributes) Console.WriteLine(a); 复制代码 ID="1243" Type="int" ConvertableTo="double" 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/a4a6b8b0-c33f-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 检索单个属性 (LINQ to XML) 示例 请参见 发送反馈意见 本主题说明在给定属性名称的情况下,如何检索元素的单个属性。 这对于编写查询表达式查找具有特定属性的元素 十分有用。 XElement 类的 Attribute 方法返回具有指定名称的 XAttribute。 示例 下面的示例使用 Attribute 方法。 本示例首先在名为 Phone 的树中查找所有后代,然后查找名为 type 的属性。 此代码生成以下输出: 如果希望检索属性的值,可以强制转换该值,就像使用 XElement 对象时一样。 下面的示例演示这一操作。 此代码生成以下输出: LINQ to XML 为 XAttribute 类提供了以下类型的显式强制转换运算符:string、bool、bool?、int、int?、 uint、uint?、long、long?、ulong、ulong?、float、float?、double、double?、decimal、decimal?、 DateTime、DateTime?、TimeSpan、TimeSpan?、GUID 和 GUID?。 下面的示例显式命名空间中的属性的相同代码。 有关更多信息,请参见使用 XML 命名空间。 C# 复制代码 XElement cust = new XElement("PhoneNumbers", new XElement("Phone", new XAttribute("type", "home"), "555-555-5555"), new XElement("Phone", new XAttribute("type", "work"), "555-555-6666") ); IEnumerable elList = from el in cust.Descendants("Phone") select el; foreach (XElement el in elList) Console.WriteLine((string)el.Attribute("type")); 复制代码 home work C# 复制代码 XElement cust = new XElement("PhoneNumbers", new XElement("Phone", new XAttribute("type", "home"), "555-555-5555"), new XElement("Phone", new XAttribute("type", "work"), "555-555-6666") ); IEnumerable elList = from el in cust.Descendants("Phone") select el; foreach (XElement el in elList) Console.WriteLine((string)el.Attribute("type")); 复制代码 home work C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XElement cust = new XElement(aw + "PhoneNumbers", new XElement(aw + "Phone", new XAttribute(aw + "type", "home"), "555-555-5555"), new XElement(aw + "Phone", 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d4ec072e-cacb-4... 请参见 此代码生成以下输出: new XAttribute(aw + "type", "work"), "555-555-6666") ); IEnumerable elList = from el in cust.Descendants(aw + "Phone") select el; foreach (XElement el in elList) Console.WriteLine((string)el.Attribute(aw + "type")); 复制代码 home work 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d4ec072e-cacb-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 检索属性的值 (LINQ to XML) 示例 请参见 发送反馈意见 本主题说明如何获取属性的值。 主要方法有两种:可以将 XAttribute 强制转换为所需的类型;然后,显式转换运算 符将元素或属性的内容转换为指定的类型。 此外,还可以使用 Value 属性。 但是,强制转换通常是更好的方法。 在检索可能存在也可能不存在的属性的值时,如果将属性强制转换为可以为 null 的类型,则代码会更易于编写。 有关此技术的示例,请参见如何: 检索元素的值 (LINQ to XML)。 示例 请参见 若要检索属性的值,只需将 XAttribute 对象强制转换为所需的类型即可。 在 Visual Basic 中,可以使用集成的属性 (Attribute) 属性 (Property) 来检索属性 (Attribute) 的值。 本示例生成以下输出: 在 Visual Basic 中,可以使用集成的属性 (Attribute) 属性 (Property) 来设置属性 (Attribute) 的值。 此外, 如果使用集成的属性 (Attribute) 属性 (Property) 来设置不存在的属性 (Attribute) 的值,则会创建该属性 (Attribute)。 本示例生成以下输出: 下面的示例演示在属性处于命名空间中时,如何检索属性的值。 有关更多信息,请参见使用 XML 命名空间。 本示例生成以下输出: C# 复制代码 XElement root = new XElement("Root", new XAttribute("Attr", "abcde") ); Console.WriteLine(root); string str = (string)root.Attribute("Attr"); Console.WriteLine(str); 复制代码 abcde Xml 复制代码 C# 复制代码 XNamespace aw = "http://www.adventure-works.com"; XElement root = new XElement(aw + "Root", new XAttribute(aw + "Attr", "abcde") ); string str = (string)root.Attribute(aw + "Attr"); Console.WriteLine(str); 复制代码 abcde 概念 LINQ to XML 轴 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/871289db-747a-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 查询 XML 树 请参见 发送反馈意见 本节提供 LINQ to XML 查询的示例。 有关编写 LINQ 查询的更多信息,请参见 Getting Started with LINQ。 在实例化 XML 树之后,编写查询是从 XML 树中提取数据的最有效方法。 另外,通过与函数构造相结合的查询,可 以生成新的 XML 文档,该文档具有与原始文档不同的形状。 本节内容 请参见 主题 说明 基本查询 (LINQ to XML) 提供查询 XML 树的常见示例。 投影和转换 (LINQ to XML) 提供从 XML 树进行投影以及转换 XML 树的常见示例。 高级查询技术 (LINQ to XML) 提供可用于更高级方案的查询技术。 针对 XPath 用户的 LINQ to XML 提供很多 XPath 表达式及其 LINQ to XML 等效表达式。 XML 的纯函数转换 提供以函数编程样式编写查询的小型教程。 概念 编程指南 (LINQ to XML) Getting Started with LINQ 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7c7b8b38-2e76-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 基本查询 (LINQ to XML) 请参见 发送反馈意见 本节提供基本 LINQ to XML 查询的示例。 本节内容 请参见 主题 说明 如何: 查找具有特定属性的元 素 演示如何查找特定的元素,该元素包含具有特定值的属性。 如何: 查找具有特定子元素的 元素 演示如何查找特定的元素,该元素包含具有特定值的子元素。 查询 XDocument 与查询 XElement 解释针对根部位于 XElement 中的 XML 树编写查询与针对根部位于 XDocument 中的 XML 树编写查询有何差异。 如何: 查找具有特定元素名称 的子代 演示如何查找元素的具有特定名称的所有子代。 该示例使用 Descendants 轴。 如何: 使用 Descendants 方 法查找单个后代 演示如何使用 Descendants 轴方法来查找单个具有唯一名称的元素。 如何: 编写使用复杂筛选的查 询 演示如何编写具有更复杂的筛选器的查询。 如何: 筛选可选元素 演示如何在形状不规则的树中查找节点。 如何: 查找命名空间中的所有 节点 演示如何查找特定命名空间中的所有节点。 如何: 对元素进行排序 演示如何编写对查询结果进行排序的查询。 如何: 对多个键上的元素排序 演示如何对多个键进行排序。 如何: 计算中间值 演示如何使用 Let 子句在 LINQ to XML 查询中计算中间值。 如何: 编写基于上下文查找元 素的查询 演示如何根据树中的其他元素来选择元素。 如何: 调试空查询结果集 显示在对针对默认命名空间中 XML 的查询进行调试时相应的修补程 序。 概念 查询 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d9ba6fb5-107b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找具有特定属性的元素 示例 请参见 发送反馈意见 本主题演示如何查找其属性具有特定值的元素。 示例 请参见 本示例演示如何查找具有值为“Billing”的 Type 属性的 Address 元素。 本示例使用下面的 XML 文档:示例 XML 文件: 典型采购订单 (LINQ to XML)。 此代码生成以下输出: 请注意,本示例的 Visual Basic 版本使用 XML Child Axis Property、XML Attribute Axis Property 和 XML Value Property。 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的典型采购单。 此代码生成以下输出: C# 复制代码 XElement root = XElement.Load("PurchaseOrder.xml"); IEnumerable address = from el in root.Elements("Address") where (string)el.Attribute("Type") == "Billing" select el; foreach (XElement el in address) Console.WriteLine(el); Xml 复制代码
Tai Yee 8 Oak Avenue Old Town PA 95819 USA
C# 复制代码 XElement root = XElement.Load("PurchaseOrderInNamespace.xml"); XNamespace aw = "http://www.adventure-works.com"; IEnumerable address = from el in root.Elements(aw + "Address") where (string)el.Attribute(aw + "Type") == "Billing" select el; foreach (XElement el in address) Console.WriteLine(el); Xml 复制代码 Tai Yee 8 Oak Avenue Old Town PA 95819 USA 概念 基本查询 (LINQ to XML) Standard Query Operators Overview Projection Operations 参考 Attribute Elements 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/620d6aea-8fc8-4d... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找具有特定子元素的元素 示例 请参见 发送反馈意见 本主题演示如何查找特定元素,该特定元素包含具有特定值的子元素。 示例 请参见 示例查找 Test 元素,该元素包含具有值为“Examp2.EXE”的 CommandLine 子元素。 本示例使用下面的 XML 文档:示例 XML 文件: 测试配置 (LINQ to XML)。 此代码生成以下输出: 请注意,本示例的 Visual Basic 版本使用 XML Child Axis Property、XML Attribute Axis Property 和 XML Value Property。 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的测试配置。 此代码生成以下输出: C# 复制代码 XElement root = XElement.Load("TestConfig.xml"); IEnumerable tests = from el in root.Elements("Test") where (string)el.Element("CommandLine") == "Examp2.EXE" select el; foreach (XElement el in tests) Console.WriteLine((string)el.Attribute("TestId")); 复制代码 0002 0006 C# 复制代码 XElement root = XElement.Load("TestConfigInNamespace.xml"); XNamespace ad = "http://www.adatum.com"; IEnumerable tests = from el in root.Elements(ad + "Test") where (string)el.Element(ad + "CommandLine") == "Examp2.EXE" select el; foreach (XElement el in tests) Console.WriteLine((string)el.Attribute("TestId")); 复制代码 0002 0006 概念 基本查询 (LINQ to XML) Standard Query Operators Overview Projection Operations 参考 Attribute Elements 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/8035f18c-6199-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 查询 XDocument 与查询 XElement 请参见 发送反馈意见 通过 XDocument.Load 加载文档时,您会注意到,您要编写的查询与通过 XElement.Load 加载文档时稍有不同。 XDocument.Load 与 XElement.Load 的比较 通过 XElement.Load 将 XML 文档加载到 XElement 中时,位于 XML 树根部的 XElement 包含所加载文档的 根元素。 然而,通过 XDocument.Load 将同一个 XML 文档加载到 XDocument 中时,树根部为 XDocument 节点,所加载文档的根元素为 XDocument 所允许的一个子 XElement 节点。 LINQ to XML 轴相对于根节点进 行操作。 第一个示例使用 Load 加载 XML 树。 然后它查询树根部的子元素。 此示例将按预期产生以下输出: 下面的示例与上面的基本相同,不同之处在于 XML 树是加载到 XDocument,而不是加载到 XElement。 此示例产生以下输出: 请注意,同样的查询返回一个 Root 节点,而不是返回三个子节点。 C# 复制代码 // Create a simple document and write it to a file File.WriteAllText("Test.xml", @" 1 2 3 "); Console.WriteLine("Querying tree loaded with XElement.Load"); Console.WriteLine("----"); XElement doc = XElement.Load("Test.xml"); IEnumerable childList = from el in doc.Elements() select el; foreach (XElement e in childList) Console.WriteLine(e); 复制代码 Querying tree loaded with XElement.Load ---- 1 2 3 C# 复制代码 // Create a simple document and write it to a file File.WriteAllText("Test.xml", @" 1 2 3 "); Console.WriteLine("Querying tree loaded with XDocument.Load"); Console.WriteLine("----"); XDocument doc = XDocument.Load("Test.xml"); IEnumerable childList = from el in doc.Elements() select el; foreach (XElement e in childList) Console.WriteLine(e); 复制代码 Querying tree loaded with XDocument.Load ---- 1 2 3 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d266f58d-f2f3-46... 请参见 处理这一问题的一种方法是在访问轴方法之前使用 Root 属性,如下所示: 此查询现在的执行方式与根部位于 XElement 中的树中的查询相同。 此示例产生以下输出: C# 复制代码 // Create a simple document and write it to a file File.WriteAllText("Test.xml", @" 1 2 3 "); Console.WriteLine("Querying tree loaded with XDocument.Load"); Console.WriteLine("----"); XDocument doc = XDocument.Load("Test.xml"); IEnumerable childList = from el in doc.Root.Elements() select el; foreach (XElement e in childList) Console.WriteLine(e); 复制代码 Querying tree loaded with XDocument.Load ---- 1 2 3 概念 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d266f58d-f2f3-46... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找具有特定元素名称的子代 示例 请参见 发送反馈意见 有时,您想要查找所有具有特定名称的子代。 可以编写代码用于循环访问所有子代,但使用 Descendants 轴更简 单。 示例 下面的示例演示如何根据元素名称查找子代。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 C# 复制代码 XElement root = XElement.Parse(@" Some text that is broken up into multiple segments. "); IEnumerable textSegs = from seg in root.Descendants("t") select (string)seg; string str = textSegs.Aggregate(new StringBuilder(), (sb, i) => sb.Append(i), sp => sp.ToString() ); Console.WriteLine(str); 复制代码 Some text that is broken up into multiple segments. C# 复制代码 XElement root = XElement.Parse(@" Some text that is broken up into multiple segments. "); XNamespace ad = "http://www.adatum.com"; IEnumerable textSegs = from seg in root.Descendants(ad + "t") select (string)seg; string str = textSegs.Aggregate(new StringBuilder(), (sb, i) => sb.Append(i), sp => sp.ToString() ); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/9a5dc46e-22a8-4... 请参见 此代码生成以下输出: Console.WriteLine(str); 复制代码 Some text that is broken up into multiple segments. 概念 基本查询 (LINQ to XML) 参考 Descendants 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/9a5dc46e-22a8-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用 Descendants 方法查找单个后代 示例 请参见 发送反馈意见 可以使用 Descendants 轴方法快速编写代码来查找名称唯一的单个元素。 如果想要查找具有特定名称的特定后代, 则此技术特别有用。 虽然可以编写代码以导航到需要的元素,但使用 Descendants 轴编写代码通常更快更容易。 示例 请参见 本示例使用 First 标准查询运算符。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 此代码生成以下输出: C# 复制代码 XElement root = XElement.Parse(@" GC1 Value GC2 Value GC3 Value GC4 Value "); string grandChild3 = (string) (from el in root.Descendants("GrandChild3") select el).First(); Console.WriteLine(grandChild3); 复制代码 GC3 Value C# 复制代码 XElement root = XElement.Parse(@" GC1 Value GC2 Value GC3 Value GC4 Value "); XNamespace aw = "http://www.adventure-works.com"; string grandChild3 = (string) (from el in root.Descendants(aw + "GrandChild3") select el).First(); Console.WriteLine(grandChild3); 复制代码 GC3 Value 概念 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/48d2ee68-2b70-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 编写使用复杂筛选的查询 示例 请参见 发送反馈意见 有时,您需要编写使用复杂筛选器的 LINQ to XML 查询。 例如,您可能必须查找其子元素具有特定名称和值的所有 元素。 本主题提供一个编写使用复杂筛选的查询的示例。 示例 请参见 本示例演示如何查找具有 Type 属性等于“Shipping”的子 Address 元素和等于“NY”的子 State 元素的所有 PurchaseOrder 元素。 示例在 Where 子句中使用嵌套查询,如果集合中有任何元素,则 Any 运算符返回 true。 有关使用基于方法的查询语法的信息,请参见Query Syntax versus Method Syntax (LINQ)。 本示例使用下面的 XML 文档: 有关 Any 运算符的更多信息,请参见Quantifier Operations (LINQ)。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的多个采购单。 此代码生成以下输出: C# 复制代码 XElement root = XElement.Load("PurchaseOrders.xml"); IEnumerable purchaseOrders = from el in root.Elements("PurchaseOrder") where (from add in el.Elements("Address") where (string)add.Attribute("Type") == "Shipping" && (string)add.Element("State") == "NY" select add) .Any() select el; foreach (XElement el in purchaseOrders) Console.WriteLine((string)el.Attribute("PurchaseOrderNumber")); 复制代码 99505 C# 复制代码 XElement root = XElement.Load("PurchaseOrdersInNamespace.xml"); XNamespace aw = "http://www.adventure-works.com"; IEnumerable purchaseOrders = from el in root.Elements(aw + "PurchaseOrder") where (from add in el.Elements(aw + "Address") where (string)add.Attribute(aw + "Type") == "Shipping" && (string)add.Element(aw + "State") == "NY" select add) .Any() select el; foreach (XElement el in purchaseOrders) Console.WriteLine((string)el.Attribute(aw + "PurchaseOrderNumber")); 复制代码 99505 概念 基本查询 (LINQ to XML) XML Child Axis Property XML Attribute Axis Property XML Value Property Quantifier Operations (LINQ) 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3b08f6ad-408d-4... Projection Operators (LINQ) 参考 Attribute Elements 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3b08f6ad-408d-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 筛选可选元素 示例 请参见 发送反馈意见 有时,尽管不能确定某个元素是否存在于 XML 文档中,您还是会尝试筛选该元素。 应当执行搜索,这样如果特定 元素没有子元素,就不会因为筛选它而触发空引用异常。 在下面的示例中,Child5 元素没有 Type 子元素,但是 查询仍可以正确执行。 示例 本示例使用 Elements 扩展方法。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 C# 复制代码 XElement root = XElement.Parse(@" Child One Text Child Two Text Child Three Text Child Four Text Child Five Text "); var cList = from typeElement in root.Elements().Elements("Type") where (string)typeElement.Attribute("Value") == "Yes" select (string)typeElement.Parent.Element("Text"); foreach(string str in cList) Console.WriteLine(str); 复制代码 Child One Text Child Two Text Child Four Text C# 复制代码 XElement root = XElement.Parse(@" Child One Text Child Two Text Child Three Text Child Four Text Child Five Text "); XNamespace ad = "http://www.adatum.com"; var cList = 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ede96cde-fbac-43... 请参见 此代码生成以下输出: from typeElement in root.Elements().Elements(ad + "Type") where (string)typeElement.Attribute("Value") == "Yes" select (string)typeElement.Parent.Element(ad + "Text"); foreach (string str in cList) Console.WriteLine(str); 复制代码 Child One Text Child Two Text Child Four Text 概念 基本查询 (LINQ to XML) XML Child Axis Property XML Attribute Axis Property XML Value Property Standard Query Operators Overview Projection Operations 参考 XElement.Attribute XContainer.Elements Extensions.Elements 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ede96cde-fbac-43... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找命名空间中的所有节点 示例 请参见 发送反馈意见 您可以对每个元素或属性的命名空间进行筛选,以便查找该特定命名空间中的所有节点。 示例 下面的示例创建一个包含两个命名空间的 XML 树。 然后循环访问该树并将打印其中一个命名空间中所有元素和属性的名 称。 此代码生成以下输出: 下面的查询所访问的 XML 文件包含两个位于不同命名空间中的采购订单。 该查询只用其中一个命名空间中的元素创建一个 新树。 本示例使用下面的 XML 文档:示例 XML 文件: 合并采购单。 此代码生成以下输出: C# 复制代码 string markup = @" abc def ghi jkl mno "; XElement xmlTree = XElement.Parse(markup); Console.WriteLine("Nodes in the http://www.adventure-works.com namespace"); IEnumerable awElements = from el in xmlTree.Descendants() where el.Name.Namespace == "http://www.adventure-works.com" select el; foreach (XElement el in awElements) Console.WriteLine(el.Name.ToString()); 复制代码 Nodes in the http://www.adventure-works.com namespace {http://www.adventure-works.com}Child3 {http://www.adventure-works.com}GrandChild2 C# 复制代码 XDocument cpo = XDocument.Load("ConsolidatedPurchaseOrders.xml"); XNamespace aw = "http://www.adventure-works.com"; XElement newTree = new XElement("Root", from el in cpo.Root.Elements() where el.Name.Namespace == aw select el ); Console.WriteLine(newTree); Xml 复制代码 Chris Preston 123 Main St. Seattle WA 98113 USA Chris Preston 123 Main St. Seattle WA 98113 USA Ship only complete order. Litware Networking Card 1 20.99 Litware 17in LCD Monitor 1 199.99 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/69468b03-4f18-4... 请参见 概念 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/69468b03-4f18-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 对元素进行排序 示例 请参见 发送反馈意见 本示例演示如何编写对查询结果进行排序的查询。 示例 请参见 本示例使用下面的 XML 文档:示例 XML 文件: 数值数据 (LINQ to XML)。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的数值数据。 此代码生成以下输出: C# 复制代码 XElement root = XElement.Load("Data.xml"); IEnumerable prices = from el in root.Elements("Data") let price = (decimal)el.Element("Price") orderby price select price; foreach (decimal el in prices) Console.WriteLine(el); 复制代码 0.99 4.95 6.99 24.50 29.00 66.00 89.99 C# 复制代码 XElement root = XElement.Load("DataInNamespace.xml"); XNamespace aw = "http://www.adatum.com"; IEnumerable prices = from el in root.Elements(aw + "Data") let price = (decimal)el.Element(aw + "Price") orderby price select price; foreach (decimal el in prices) Console.WriteLine(el); 复制代码 0.99 4.95 6.99 24.50 29.00 66.00 89.99 概念 Sorting Data 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/a8632b2e-529e-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 对多个键上的元素排序 示例 请参见 发送反馈意见 本主题演示如何对多个键进行排序。 示例 在本示例中,首先按运输邮政编码,然后再按订单日期对结果进行排序。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的客户和订单。 此代码生成以下输出: C# 复制代码 XElement co = XElement.Load("CustomersOrders.xml"); var sortedElements = from c in co.Element("Orders").Elements("Order") orderby (string)c.Element("ShipInfo").Element("ShipPostalCode"), (DateTime)c.Element("OrderDate") select new { CustomerID = (string)c.Element("CustomerID"), EmployeeID = (string)c.Element("EmployeeID"), ShipPostalCode = (string)c.Element("ShipInfo").Element("ShipPostalCode"), OrderDate = (DateTime)c.Element("OrderDate") }; foreach (var r in sortedElements) Console.WriteLine("CustomerID:{0} EmployeeID:{1} ShipPostalCode:{2} OrderDate:{3:d}", r.CustomerID, r.EmployeeID, r.ShipPostalCode, r.OrderDate); 复制代码 CustomerID:LETSS EmployeeID:1 ShipPostalCode:94117 OrderDate:6/25/1997 CustomerID:LETSS EmployeeID:8 ShipPostalCode:94117 OrderDate:10/27/1997 CustomerID:LETSS EmployeeID:6 ShipPostalCode:94117 OrderDate:11/10/1997 CustomerID:LETSS EmployeeID:4 ShipPostalCode:94117 OrderDate:2/12/1998 CustomerID:GREAL EmployeeID:6 ShipPostalCode:97403 OrderDate:5/6/1997 CustomerID:GREAL EmployeeID:8 ShipPostalCode:97403 OrderDate:7/4/1997 CustomerID:GREAL EmployeeID:1 ShipPostalCode:97403 OrderDate:7/31/1997 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:7/31/1997 CustomerID:GREAL EmployeeID:6 ShipPostalCode:97403 OrderDate:9/4/1997 CustomerID:GREAL EmployeeID:3 ShipPostalCode:97403 OrderDate:9/25/1997 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:1/6/1998 CustomerID:GREAL EmployeeID:3 ShipPostalCode:97403 OrderDate:3/9/1998 CustomerID:GREAL EmployeeID:3 ShipPostalCode:97403 OrderDate:4/7/1998 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:4/22/1998 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:4/30/1998 CustomerID:HUNGC EmployeeID:3 ShipPostalCode:97827 OrderDate:12/6/1996 CustomerID:HUNGC EmployeeID:1 ShipPostalCode:97827 OrderDate:12/25/1996 CustomerID:HUNGC EmployeeID:3 ShipPostalCode:97827 OrderDate:1/15/1997 CustomerID:HUNGC EmployeeID:4 ShipPostalCode:97827 OrderDate:7/16/1997 CustomerID:HUNGC EmployeeID:8 ShipPostalCode:97827 OrderDate:9/8/1997 CustomerID:LAZYK EmployeeID:1 ShipPostalCode:99362 OrderDate:3/21/1997 CustomerID:LAZYK EmployeeID:8 ShipPostalCode:99362 OrderDate:5/22/1997 C# 复制代码 XElement co = XElement.Load("CustomersOrdersInNamespace.xml"); XNamespace aw = "http://www.adventure-works.com"; var sortedElements = from c in co.Element(aw + "Orders").Elements(aw + "Order") orderby (string)c.Element(aw + "ShipInfo").Element(aw + "ShipPostalCode"), (DateTime)c.Element(aw + "OrderDate") select new { CustomerID = (string)c.Element(aw + "CustomerID"), EmployeeID = (string)c.Element(aw + "EmployeeID"), ShipPostalCode = (string)c.Element(aw + "ShipInfo").Element(aw + "ShipPostalCode"), OrderDate = (DateTime)c.Element(aw + "OrderDate") }; foreach (var r in sortedElements) Console.WriteLine("CustomerID:{0} EmployeeID:{1} ShipPostalCode:{2} OrderDate:{3:d}", r.CustomerID, r.EmployeeID, r.ShipPostalCode, r.OrderDate); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3a3a988e-d267-4... 请参见 复制代码 CustomerID:LETSS EmployeeID:1 ShipPostalCode:94117 OrderDate:6/25/1997 CustomerID:LETSS EmployeeID:8 ShipPostalCode:94117 OrderDate:10/27/1997 CustomerID:LETSS EmployeeID:6 ShipPostalCode:94117 OrderDate:11/10/1997 CustomerID:LETSS EmployeeID:4 ShipPostalCode:94117 OrderDate:2/12/1998 CustomerID:GREAL EmployeeID:6 ShipPostalCode:97403 OrderDate:5/6/1997 CustomerID:GREAL EmployeeID:8 ShipPostalCode:97403 OrderDate:7/4/1997 CustomerID:GREAL EmployeeID:1 ShipPostalCode:97403 OrderDate:7/31/1997 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:7/31/1997 CustomerID:GREAL EmployeeID:6 ShipPostalCode:97403 OrderDate:9/4/1997 CustomerID:GREAL EmployeeID:3 ShipPostalCode:97403 OrderDate:9/25/1997 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:1/6/1998 CustomerID:GREAL EmployeeID:3 ShipPostalCode:97403 OrderDate:3/9/1998 CustomerID:GREAL EmployeeID:3 ShipPostalCode:97403 OrderDate:4/7/1998 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:4/22/1998 CustomerID:GREAL EmployeeID:4 ShipPostalCode:97403 OrderDate:4/30/1998 CustomerID:HUNGC EmployeeID:3 ShipPostalCode:97827 OrderDate:12/6/1996 CustomerID:HUNGC EmployeeID:1 ShipPostalCode:97827 OrderDate:12/25/1996 CustomerID:HUNGC EmployeeID:3 ShipPostalCode:97827 OrderDate:1/15/1997 CustomerID:HUNGC EmployeeID:4 ShipPostalCode:97827 OrderDate:7/16/1997 CustomerID:HUNGC EmployeeID:8 ShipPostalCode:97827 OrderDate:9/8/1997 CustomerID:LAZYK EmployeeID:1 ShipPostalCode:99362 OrderDate:3/21/1997 CustomerID:LAZYK EmployeeID:8 ShipPostalCode:99362 OrderDate:5/22/1997 概念 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3a3a988e-d267-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 计算中间值 示例 请参见 发送反馈意见 本示例演示如何计算可用于进行排序、筛选和选择的中间值。 示例 请参见 下面的示例使用 Let 子句。 本示例使用下面的 XML 文档:示例 XML 文件: 数值数据 (LINQ to XML)。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 本示例使用下面的 XML 文档:示例 XML 文件: 命名空间中的数值数据。 此代码生成以下输出: C# 复制代码 XElement root = XElement.Load("Data.xml"); IEnumerable extensions = from el in root.Elements("Data") let extension = (decimal)el.Element("Quantity") * (decimal)el.Element("Price") where extension >= 25 orderby extension select extension; foreach (decimal ex in extensions) Console.WriteLine(ex); 复制代码 55.92 73.50 89.99 198.00 435.00 C# 复制代码 XElement root = XElement.Load("DataInNamespace.xml"); XNamespace ad = "http://www.adatum.com"; IEnumerable extensions = from el in root.Elements(ad + "Data") let extension = (decimal)el.Element(ad + "Quantity") * (decimal)el.Element(ad + "Price") where extension >= 25 orderby extension select extension; foreach (decimal ex in extensions) Console.WriteLine(ex); 复制代码 55.92 73.50 89.99 198.00 435.00 概念 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/46863b67-9c02-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 编写基于上下文查找元素的查询 示例 请参见 发送反馈意见 有时,您可能必须编写基于元素上下文选择元素的查询。 您可能需要基于前面或后面的同级元素进行筛选。 您可 能需要基于子元素或上级元素进行筛选。 通过编写查询并在 where 子句中使用查询的结果可以实现此目的。 如果在测试值之前必须先测试空值,则更适合 在 let 子句中执行查询,然后在 where 子句中使用查询结果。 示例 下面的示例选择后面紧接 ul 元素的所有 p 元素。 此代码生成以下输出: 下面的示例演示如何对命名空间中的 XML 进行同样的查询。 有关更多信息,请参见使用 XML 命名空间。 C# 复制代码 XElement doc = XElement.Parse(@"

    abc

    def

    abc

"); IEnumerable items = from e in doc.Descendants("p") let z = e.ElementsAfterSelf().FirstOrDefault() where z != null && z.Name.LocalName == "ul" select e; foreach (XElement e in items) Console.WriteLine("id = {0}", (string)e.Attribute("id")); 复制代码 id = 1 id = 3 id = 6 C# 复制代码 XElement doc = XElement.Parse(@"

    abc

    def

    abc

"); XNamespace ad = "http://www.adatum.com"; 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/14c2c22d-8400-4... 请参见 此代码生成以下输出: IEnumerable items = from e in doc.Descendants(ad + "p") let z = e.ElementsAfterSelf().FirstOrDefault() where z != null && z.Name == ad.GetName("ul") select e; foreach (XElement e in items) Console.WriteLine("id = {0}", (string)e.Attribute("id")); 复制代码 id = 1 id = 3 id = 6 概念 基本查询 (LINQ to XML) 参考 Parse Descendants ElementsAfterSelf FirstOrDefault 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/14c2c22d-8400-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 调试空查询结果集 示例 请参见 发送反馈意见 查询 XML 树时遇到的一个最常见问题是,如果 XML 树具有默认命名空间,开发人员在编写查询时,有时会将 XML 视为不在命名空间内。 本主题的第一个示例集演示一种加载并按不正确方式查询默认命名空间中的 XML 的典型方式。 第二个示例集演示必需的更正,以便可以查询命名空间中的 XML。 有关更多信息,请参见使用 XML 命名空间。 示例 此示例演示如何在命名空间中创建 XML 和一个返回空结果集的查询。 此示例产生下面的结果: 本示例演示如何在命名空间中创建 XML 和一个正确编码的查询。 在使用 C# 时,解决方法是声明和初始化一个 XNamespace 对象,并在指定 XName 对象时使用它。 在这种 情况下,Elements 方法的参数是一个 XName 对象。 而在使用 Visual Basic 时,解决方法是声明和初始化一个全局默认命名空间。 这样会将所有 XML 属性放入该 默认命名空间。 无需对该示例做任何其他修改,即可使它正常运行。 此示例产生下面的结果: C# 复制代码 XElement root = XElement.Parse( @" 1 2 3 4 5 6 "); IEnumerable c1 = from el in root.Elements("Child") select el; Console.WriteLine("Result set follows:"); foreach (XElement el in c1) Console.WriteLine((int)el); Console.WriteLine("End of result set"); 复制代码 Result set follows: End of result set C# 复制代码 XElement root = XElement.Parse( @" 1 2 3 4 5 6 "); XNamespace aw = "http://www.adventure-works.com"; IEnumerable c1 = from el in root.Elements(aw + "Child") select el; Console.WriteLine("Result set follows:"); foreach (XElement el in c1) Console.WriteLine((int)el); Console.WriteLine("End of result set"); 复制代码 Result set follows: 1 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/96630ab8-56f6-4... 请参见 2 3 End of result set 概念 基本查询 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/96630ab8-56f6-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 投影和转换 (LINQ to XML) 请参见 发送反馈意见 本节提供 LINQ to XML 投影和转换的示例。 本节内容 请参见 主题 说明 如何: 通过 LINQ to XML 使用字典 演示如何将字典转换为 XML,以及如何将 XML 转换为字典。 如何: 转换 XML 树的形状 演示如何转换 XML 文档的形状。 如何: 控制投影的类型 演示如何控制 LINQ to XML 查询的类型。 如何: 投影新类型 (LINQ to XML) 演示如何从 LINQ to XML 查询投影用户定义类型的集合。 如何: 投影对象图 演示如何从 LINQ to XML 查询投影更复杂的对象图。 如何: 投影匿名类型 演示如何从 LINQ to XML 查询投影匿名对象的集合。 如何: 从 XML 生成文本文件 演示如何将 XML 文件转换为非 XML 文本文件。 如何: 从 CSV 文件生成 XML 演示如何使用 LINQ 分析 CSV 文件并从它生成 XML。 概念 查询 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ad35aec5-f55a-41... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 通过 LINQ to XML 使用字典 示例 请参见 发送反馈意见 通常需要将各种数据结构转换为 XML 和将 XML 转换回其他数据结构。 本主题通过 Dictionary(TKey, TValue) 和 XML 的相互转换演示这一常规方法的具体实现。 示例 请参见 本示例的 C# 版本使用函数构造形式:查询投影新 XElement 对象,生成的集合作为参数传递给根 XElement 对象的构造函数。 本示例的 Visual Basic 版本在嵌入式表达式中使用 XML 文本和查询。 查询投影新 XElement 对象,该对象然 后成为 RootXElement 对象的新内容。 此代码生成以下输出: 下面的代码从 XML 创建一个字典。 此代码生成以下输出: C# 复制代码 Dictionary dict = new Dictionary(); dict.Add("Child1", "Value1"); dict.Add("Child2", "Value2"); dict.Add("Child3", "Value3"); dict.Add("Child4", "Value4"); XElement root = new XElement("Root", from keyValue in dict select new XElement(keyValue.Key, keyValue.Value) ); Console.WriteLine(root); Xml 复制代码 Value1 Value2 Value3 Value4 C# 复制代码 XElement root = new XElement("Root", new XElement("Child1", "Value1"), new XElement("Child2", "Value2"), new XElement("Child3", "Value3"), new XElement("Child4", "Value4") ); Dictionary dict = new Dictionary(); foreach (XElement el in root.Elements()) dict.Add(el.Name.LocalName, el.Value); foreach (string str in dict.Keys) Console.WriteLine("{0}:{1}", str, dict[str]); 复制代码 Child1:Value1 Child2:Value2 Child3:Value3 Child4:Value4 概念 投影和转换 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0df43299-5aa7-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 转换 XML 树的形状 示例 请参见 发送反馈意见 XML 文档的形状是指它的元素名称、属性名称以及它的层次结构的特征。 有时,您将不得不更改 XML 文档的形状。 例如,您可能必须将一个现有 XML 文档发送到另一个系统,而该系统要求使用 不同的元素和属性名称。 您可以在整个文档中,根据需要删除和重命名元素,但是,如果使用函数构造,则可获得可读性 更强、更易于维护的代码。 有关函数构造的更多信息,请参见函数构造 (LINQ to XML)。 第一个示例更改 XML 文档的组织结构。 它将复杂元素从树中的一个位置移动到另一个位置。 本主题的第二个示例创建一个 XML 文档,该文档具有与源文档不同的形状。 它更改元素名称的大小写,重命名某些元素, 转换后的树中不包含源树的某些元素。 示例 下面的代码使用嵌入式查询表达式更改 XML 文件的形状。 本示例中的源 XML 文档在 Root 元素(它包含所有客户)下包含一个 Customers 元素。 此外,在 Root 元素(包含 所有订单)下包含一个 Orders 元素。 本示例创建一个新的 XML 树,在该树中,每个客户的订单都包含在 Customer 元素内的 Orders 元素中。 原始文档还在 Order 元素中包含一个 CustomerID 元素;此元素将从重新变 形的文档中移除。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 此代码生成以下输出: C# 复制代码 XElement co = XElement.Load("CustomersOrders.xml"); XElement newCustOrd = new XElement("Root", from cust in co.Element("Customers").Elements("Customer") select new XElement("Customer", cust.Attributes(), cust.Elements(), new XElement("Orders", from ord in co.Element("Orders").Elements("Order") where (string)ord.Element("CustomerID") == (string)cust.Attribute("CustomerID") select new XElement("Order", ord.Attributes(), ord.Element("EmployeeID"), ord.Element("OrderDate"), ord.Element("RequiredDate"), ord.Element("ShipInfo") ) ) ) ); Console.WriteLine(newCustOrd); Xml 复制代码 Great Lakes Food Market Howard Snyder Marketing Manager (503) 555-7555

2732 Baker Blvd.
Eugene OR 97403 USA
Hungry Coyote Import Store Yoshi Latimer Sales Representative (503) 555-6874 (503) 555-2376
City Center Plaza 516 Main St.
Elgin OR 97827 USA
页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/bd6ae47e-0d6f-4... 请参见 本示例重命名某些元素并将某些属性转换为元素。 代码调用 ConvertAddress,它返回一个 XElement 对象列表。 此方法的参数是一个查询,该查询确定 Type 属性值 为 "Shipping" 的 Address 复杂元素。 本示例使用下面的 XML 文档:示例 XML 文件: 典型采购订单 (LINQ to XML)。 此代码生成以下输出:
. . . C# 复制代码 static IEnumerable ConvertAddress(XElement add) { List fragment = new List() { new XElement("NAME", (string)add.Element("Name")), new XElement("STREET", (string)add.Element("Street")), new XElement("CITY", (string)add.Element("City")), new XElement("ST", (string)add.Element("State")), new XElement("POSTALCODE", (string)add.Element("Zip")), new XElement("COUNTRY", (string)add.Element("Country")) }; return fragment; } static void Main(string[] args) { XElement po = XElement.Load("PurchaseOrder.xml"); XElement newPo = new XElement("PO", new XElement("ID", (string)po.Attribute("PurchaseOrderNumber")), new XElement("DATE", (DateTime)po.Attribute("OrderDate")), ConvertAddress( (from el in po.Elements("Address") where (string)el.Attribute("Type") == "Shipping" select el) .First() ) ); Console.WriteLine(newPo); } Xml 复制代码 99503 1999-10-20T00:00:00 Ellen Adams 123 Maple Street Mill Valley CA 10999 USA 概念 投影和转换 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/bd6ae47e-0d6f-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 控制投影的类型 示例 请参见 发送反馈意见 投影是一个过程,这一过程包括:获取一组数据,筛选这些数据,更改数据形状,甚至更改数据的类型。 大多数查询表达式都 可执行投影。 本节中介绍的大多数查询表达式的计算结果都是 XElement 的 IEnumerable(T),不过,可以控制投影的类型从 而创建其他类型的集合。 本主题演示如何执行此操作。 示例 请参见 下面的示例定义一个新类型 Customer。 然后,查询表达式在 Select 子句中实例化新的 Customer 对象。 这样,查询 表达式的类型就是 Customer 的 IEnumerable(T)。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 此代码生成以下输出: C# 复制代码 public class Customer { private string customerID; public string CustomerID{ get{return customerID;} set{customerID = value;}} private string companyName; public string CompanyName{ get{return companyName;} set{companyName = value;}} private string contactName; public string ContactName { get{return contactName;} set{contactName = value;}} public Customer(string customerID, string companyName, string contactName) { this.customerID = customerID; this.companyName = companyName; this.contactName = contactName; } public override string ToString() { return String.Format("{0}:{1}:{2}", this.customerID, this.companyName, this.contactName); } } class Program { static void Main(string[] args) { XElement custOrd = XElement.Load("CustomersOrders.xml"); IEnumerable custList = from el in custOrd.Element("Customers").Elements("Customer") select new Customer( (string)el.Attribute("CustomerID"), (string)el.Element("CompanyName"), (string)el.Element("ContactName") ); foreach (Customer cust in custList) Console.WriteLine(cust); } } 复制代码 GREAL:Great Lakes Food Market:Howard Snyder HUNGC:Hungry Coyote Import Store:Yoshi Latimer LAZYK:Lazy K Kountry Store:John Steel LETSS:Let's Stop N Shop:Jaime Yorres 概念 投影和转换 (LINQ to XML) 参考 Select 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/8ecc7ec8-4189-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 投影新类型 (LINQ to XML) 示例 请参见 发送反馈意见 本节中的其他示例已演示了一些查询,这些查询以 XElement 的 IEnumerable(T)、string 的 IEnumerable(T) 和 int 的 IEnumerable(T) 的形式返回结果。 这些是常见结果类型,但它们不是对所有情况都适用。 在很多情况下,会希 望查询返回其他类型的 IEnumerable(T)。 示例 请参见 此示例演示如何在 select 子句中实例化对象。 代码首先定义一个具有一个构造函数的新类,然后修改 select 语句,使该表达式成为新类的新实例。 本示例使用下面的 XML 文档:示例 XML 文件: 典型采购订单 (LINQ to XML)。 本示例使用主题如何: 检索单个子元素 (LINQ to XML)中介绍的 M:System.Xml.Linq.XElement.Element 方法。 还使用强制转换来检索 M:System.Xml.Linq.XElement.Element 方法返回的元素值。 本示例生成以下输出: C# 复制代码 class NameQty { public string name; public int qty; public NameQty(string n, int q) { name = n; qty = q; } }; class Program { public static void Main() { XElement po = XElement.Load("PurchaseOrder.xml"); IEnumerable nqList = from n in po.Descendants("Item") select new NameQty( (string)n.Element("ProductName"), (int)n.Element("Quantity") ); foreach (NameQty n in nqList) Console.WriteLine(n.name + ":" + n.qty); } } 复制代码 Lawnmower:1 Baby Monitor:2 概念 投影和转换 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/79888cba-3df9-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 投影对象图 示例 请参见 发送反馈意见 本主题演示如何从 XML 投影或填充对象图。 示例 下面的代码用示例 XML 文件: 典型采购订单 (LINQ to XML) XML 文档中的 Address、PurchaseOrder 和 PurchaseOrderItem 类填充对象图。 C# 复制代码 class Address { public enum AddressUse { Shipping, Billing, } private AddressUse addressType; private string name; private string street; private string city; private string state; private string zip; private string country; public AddressUse AddressType { get { return addressType; } set { addressType = value; } } public string Name { get { return name; } set { name = value; } } public string Street { get { return street; } set { street = value; } } public string City { get { return city; } set { city = value; } } public string State { get { return state; } set { state = value; } } public string Zip { get { return zip; } set { zip = value; } } public string Country { get { return country; } set { country = value; } } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append(String.Format("Type: {0}\n", addressType == AddressUse.Shipping ? "Shipping" : "Billing")); sb.Append(String.Format("Name: {0}\n", name)); sb.Append(String.Format("Street: {0}\n", street)); sb.Append(String.Format("City: {0}\n", city)); sb.Append(String.Format("State: {0}\n", state)); sb.Append(String.Format("Zip: {0}\n", zip)); sb.Append(String.Format("Country: {0}\n", country)); return sb.ToString(); } } class PurchaseOrderItem { private string partNumber; private string productName; private int quantity; private Decimal usPrice; private string comment; private DateTime shipDate; 页码,1/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/75bacf34-1774-4... public string PartNumber { get { return partNumber; } set { partNumber = value; } } public string ProductName { get { return productName; } set { productName = value; } } public int Quantity { get { return quantity; } set { quantity = value; } } public Decimal USPrice { get { return usPrice; } set { usPrice = value; } } public string Comment { get { return comment; } set { comment = value; } } public DateTime ShipDate { get { return shipDate; } set { shipDate = value; } } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append(String.Format("PartNumber: {0}\n", partNumber)); sb.Append(String.Format("ProductName: {0}\n", productName)); sb.Append(String.Format("Quantity: {0}\n", quantity)); sb.Append(String.Format("USPrice: {0}\n", usPrice)); if (comment != null) sb.Append(String.Format("Comment: {0}\n", comment)); if (shipDate != DateTime.MinValue) sb.Append(String.Format("ShipDate: {0:d}\n", shipDate)); return sb.ToString(); } } class PurchaseOrder { private string purchaseOrderNumber; private DateTime orderDate; private string comment; private List
addresses; private List items; public string PurchaseOrderNumber { get { return purchaseOrderNumber; } set { purchaseOrderNumber = value; } } public DateTime OrderDate { get { return orderDate; } set { orderDate = value; } } public string Comment { get { return comment; } set { comment = value; } } public List
Addresses { get { return addresses; } set { addresses = value; } } public List Items { get { return items; } set { items = value; } } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append(String.Format("PurchaseOrderNumber: {0}\n", purchaseOrderNumber)); sb.Append(String.Format("OrderDate: {0:d}\n", orderDate)); sb.Append("\n"); sb.Append("Addresses\n"); sb.Append("=====\n"); foreach (Address address in addresses) { sb.Append(address); sb.Append("\n"); } sb.Append("Items\n"); sb.Append("=====\n"); foreach (PurchaseOrderItem item in items) 页码,2/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/75bacf34-1774-4... 在此示例中,LINQ 查询的结果以 PurchaseOrderItem 的 IEnumerable(T) 形式返回。 PurchaseOrder 类中各项 的类型都是 PurchaseOrderItem 的 IEnumerable(T)。 该代码使用 ToList(TSource) 扩展方法,根据查询结果来 创建 List(T) 集合。 该示例生成以下输出: { sb.Append(item); sb.Append("\n"); } return sb.ToString(); } } class Program { public static void Main() { XElement po = XElement.Load("PurchaseOrder.xml"); PurchaseOrder purchaseOrder = new PurchaseOrder { PurchaseOrderNumber = (string)po.Attribute("PurchaseOrderNumber"), OrderDate = (DateTime)po.Attribute("OrderDate"), Addresses = ( from a in po.Elements("Address") select new Address { AddressType = ((string)a.Attribute("Type") == "Shipping") ? Address.AddressUse.Shipping : Address.AddressUse.Billing, Name = (string)a.Element("Name"), Street = (string)a.Element("Street"), City = (string)a.Element("City"), State = (string)a.Element("State"), Zip = (string)a.Element("Zip"), Country = (string)a.Element("Country") } ).ToList(), Items = ( from i in po.Element("Items").Elements("Item") select new PurchaseOrderItem { PartNumber = (string)i.Attribute("PartNumber"), ProductName = (string)i.Element("ProductName"), Quantity = (int)i.Element("Quantity"), USPrice = (Decimal)i.Element("USPrice"), Comment = (string)i.Element("Comment"), ShipDate = (i.Element("ShipDate") != null) ? (DateTime)i.Element("ShipDate") : DateTime.MinValue } ).ToList() }; Console.WriteLine(purchaseOrder); } } 复制代码 PurchaseOrderNumber: 99503 OrderDate: 10/20/1999 Addresses ===== Type: Shipping Name: Ellen Adams Street: 123 Maple Street City: Mill Valley State: CA Zip: 10999 Country: USA Type: Billing Name: Tai Yee Street: 8 Oak Avenue City: Old Town State: PA Zip: 95819 Country: USA Items ===== PartNumber: 872-AA ProductName: Lawnmower Quantity: 1 USPrice: 148.95 Comment: Confirm this is electric 页码,3/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/75bacf34-1774-4... 请参见 PartNumber: 926-AA ProductName: Baby Monitor Quantity: 2 USPrice: 39.98 ShipDate: 5/21/1999 概念 投影和转换 (LINQ to XML) 参考 Select ToList(TSource) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/75bacf34-1774-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 投影匿名类型 示例 请参见 发送反馈意见 在某些情况下,您可能需要将查询投影到新类型,即使您知道只是短时间使用此类型。 创建仅在投影中使用的新类型需 要大量额外工作。 在这种情况下,一种更有效的方法是投影到匿名类型。 匿名类型允许您定义一个类,然后在不给出 类名称的情况下声明并初始化该类的对象。 匿名类型是“元组”这一数学概念的 C# 实现。 数学术语元组源自序列单元组、双元组、三元组、四元组、五元组和 n 元组。 它指有限的对象序列,每个对象具有特定的类型。 有时,它称为名称/值对的列表。 例如,典型采购订单 XML 文档中某一地址的内容可表示为如下形式: 在创建匿名类型的实例时,可以将其想像为创建 n 阶元组。如果编写一个将在 select 子句中创建元组的查询,该查询 将返回该元组的一个 IEnumerable。 示例 请参见 复制代码 Name: Ellen Adams Street: 123 Maple Street City: Mill Valley State: CA Zip: 90952 Country: USA 在此示例中,select 子句投影一个匿名类型。 然后,示例使用 var 创建 IEnumerable 对象。 在 foreach 循 环中,该迭代变量成为在查询表达式中创建的匿名类型的实例。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 此代码生成以下输出: C# 复制代码 XElement custOrd = XElement.Load("CustomersOrders.xml"); var custList = from el in custOrd.Element("Customers").Elements("Customer") select new { CustomerID = (string)el.Attribute("CustomerID"), CompanyName = (string)el.Element("CompanyName"), ContactName = (string)el.Element("ContactName") }; foreach (var cust in custList) Console.WriteLine("{0}:{1}:{2}", cust.CustomerID, cust.CompanyName, cust.ContactName); 复制代码 GREAL:Great Lakes Food Market:Howard Snyder HUNGC:Hungry Coyote Import Store:Yoshi Latimer LAZYK:Lazy K Kountry Store:John Steel LETSS:Let's Stop N Shop:Jaime Yorres 概念 投影和转换 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/34c432ce-66a8-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 从 XML 生成文本文件 示例 请参见 发送反馈意见 本示例演示如何从 XML 文件生成逗号分隔值 (CSV) 文件。 示例 请参见 本示例的 C# 版本使用方法语法和 Aggregate 运算符通过一个表达式从 XML 文档生成 CSV 文件。 有关更多信息,请参见Query Syntax versus Method Syntax (LINQ)。 Visual Basic 版本使用过程代码将字符串集合聚合为一个字符串。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 此代码生成以下输出: C# 复制代码 XElement custOrd = XElement.Load("CustomersOrders.xml"); string csv = (from el in custOrd.Element("Customers").Elements("Customer") select String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}{10}", (string)el.Attribute("CustomerID"), (string)el.Element("CompanyName"), (string)el.Element("ContactName"), (string)el.Element("ContactTitle"), (string)el.Element("Phone"), (string)el.Element("FullAddress").Element("Address"), (string)el.Element("FullAddress").Element("City"), (string)el.Element("FullAddress").Element("Region"), (string)el.Element("FullAddress").Element("PostalCode"), (string)el.Element("FullAddress").Element("Country"), Environment.NewLine ) ) .Aggregate( new StringBuilder(), (sb, s) => sb.Append(s), sb => sb.ToString() ); Console.WriteLine(csv); 复制代码 GREAL,Great Lakes Food Market,Howard Snyder,Marketing Manager,(503) 555-7555,2732 Baker Blvd.,Eugene,OR,97403,USA HUNGC,Hungry Coyote Import Store,Yoshi Latimer,Sales Representative,(503) 555-6874,City Center Plaza 516 Main St.,Elgin,OR,97827,USA LAZYK,Lazy K Kountry Store,John Steel,Marketing Manager,(509) 555-7969,12 Orchestra Terrace,Walla Walla,WA,99362,USA LETSS,Let's Stop N Shop,Jaime Yorres,Owner,(415) 555-5938,87 Polk St. Suite 5,San Francisco,CA,94117,USA 概念 投影和转换 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e3e09594-0e3c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 从 CSV 文件生成 XML 示例 请参见 发送反馈意见 本示例演示如何使用 Language-Integrated Query (LINQ) 和 LINQ to XML 从逗号分隔值 (CSV) 文件生成 XML 文件。 示例 请参见 下面的代码对字符串数组执行 LINQ 查询。 在 C# 版本中,该查询使用 let 子句将每个字符串分隔成字段数组。 此代码生成以下输出: C# 复制代码 // Create the text file. string csvString = @"GREAL,Great Lakes Food Market,Howard Snyder,Marketing Manager,(503) 555-7555,2732 Baker Blvd.,Eugene,OR,97403,USA HUNGC,Hungry Coyote Import Store,Yoshi Latimer,Sales Representative,(503) 555-6874,City Center Plaza 516 Main St.,Elgin,OR,97827,USA LAZYK,Lazy K Kountry Store,John Steel,Marketing Manager,(509) 555-7969,12 Orchestra Terrace,Walla Walla,WA,99362,USA LETSS,Let's Stop N Shop,Jaime Yorres,Owner,(415) 555-5938,87 Polk St. Suite 5,San Francisco,CA,94117,USA"; File.WriteAllText("cust.csv", csvString); // Read into an array of strings. string[] source = File.ReadAllLines("cust.csv"); XElement cust = new XElement("Root", from str in source let fields = str.Split(',') select new XElement("Customer", new XAttribute("CustomerID", fields[0]), new XElement("CompanyName", fields[1]), new XElement("ContactName", fields[2]), new XElement("ContactTitle", fields[3]), new XElement("Phone", fields[4]), new XElement("FullAddress", new XElement("Address", fields[5]), new XElement("City", fields[6]), new XElement("Region", fields[7]), new XElement("PostalCode", fields[8]), new XElement("Country", fields[9]) ) ) ); Console.WriteLine(cust); Xml 复制代码 Great Lakes Food Market Howard Snyder Marketing Manager (503) 555-7555
2732 Baker Blvd.
Eugene OR 97403 USA
Hungry Coyote Import Store Yoshi Latimer Sales Representative (503) 555-6874
City Center Plaza 516 Main St.
Elgin OR 97827 USA
Lazy K Kountry Store John Steel Marketing Manager (509) 555-7969
12 Orchestra Terrace
Walla Walla WA 99362 USA
Let's Stop N Shop Jaime Yorres Owner (415) 555-5938
87 Polk St. Suite 5
San Francisco CA 94117 USA
概念 投影和转换 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/dd7bab8c-96fa-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 高级查询技术 (LINQ to XML) 请参见 发送反馈意见 本节提供更多高级 LINQ to XML 查询技术的示例。 本节内容 请参见 主题 说明 如何: 联接两个集合 (C#) (LINQ to XML) 演示如何使用 Join 子句来利用 XML 数据中的关系。 如何: 使用分组创建层次结构 演示如何将数据分组,再基于分组生成 XML。 如何: 使用 XPath 查询 LINQ to XML 演示如何基于 XPath 查询来检索集合。 如何: 编写 LINQ to XML 轴 方法 演示如何编写 LINQ to XML 轴方法。 如何: 执行文本到 XML 的流 式转换 演示如何在保持很小的内存需求量的同时将非常大的文本文件转换为 XML。 如何: 列出树中的所有节点 演示一种用于列出 XML 树中所有节点的实用工具方法。 此方法可用于 调试修改 XML 树的代码。 如何: 从 Office Open XML 文 档检索段落 演示打开 Office Open XML 文档;检索 XElement 对象集合中的段落、 段落文本和段落样式的代码。 如何: 修改 Office Open XML 文档 演示打开、修改和保存 Office Open XML 文档的代码。 如何: 从文件系统填充 XML 树 演示从文件系统创建 XML 树的代码。 概念 查询 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/32e7e02e-cd96-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 联接两个集合 (C#) (LINQ to XML) 示例 请参见 发送反馈意见 XML 文档中的元素或属性有时可以引用另一个其他元素或属性。 例如,客户和订单 XML 文档包含一个客户列表和 一个订单列表。 每个 Customer 元素都包含一个 CustomerID 属性。 每个 Order 元素都包含一个 CustomerID 元素。 每个订单中的 CustomerID 元素都引用客户中的 CustomerID 属性。 主题示例 XSD 文件: 客户和订单包含一个用于验证此文档的 XSD。 它使用 XSD 的 xs:key 和 xs:keyref 功 能,将 Customer 元素的 CustomerID 属性设置为键,并在每个 Order 元素的 CustomerID 元素和每个 Customer 元素的 CustomerID 属性之间建立关系。 使用 LINQ to XML,可以通过使用 join 子句利用这种关系。 请注意,由于没有可用的索引,这种联接的运行时性能较差。 有关 join 的详尽信息,请参见Joining。 示例 下面的示例将 Customer 元素与 Order 元素联接在一起,并生成一个新的 XML 文档,该文档包含订单中的 CompanyName 元素。 执行查询之前,示例确认文档符合示例 XSD 文件: 客户和订单中的架构。 这样可确保联接子句始终能运 行。 此查询首先检索所有 Customer 元素,然后将它们联接到 Order 元素。 此查询仅选择 CustomerID 大于 “K”的客户的订单。 然后投影一个新的 Order 元素,该元素包含每个订单内的客户信息。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 本示例使用下面的 XSD 架构:示例 XSD 文件: 客户和订单。 请注意,这种方式的联接将不能很好地执行。 联接是通过线性搜索执行的。 没有任何哈希表或索引来帮助改 善性能。 C# 复制代码 XmlSchemaSet schemas = new XmlSchemaSet(); schemas.Add("", "CustomersOrders.xsd"); Console.Write("Attempting to validate, "); XDocument custOrdDoc = XDocument.Load("CustomersOrders.xml"); bool errors = false; custOrdDoc.Validate(schemas, (o, e) => { Console.WriteLine("{0}", e.Message); errors = true; }); Console.WriteLine("custOrdDoc {0}", errors ? "did not validate" : "validated"); if (!errors) { // Join customers and orders, and create a new XML document with // a different shape. // The new document contains orders only for customers with a // CustomerID > 'K' XElement custOrd = custOrdDoc.Element("Root"); XElement newCustOrd = new XElement("Root", from c in custOrd.Element("Customers").Elements("Customer") join o in custOrd.Element("Orders").Elements("Order") on (string)c.Attribute("CustomerID") equals (string)o.Element("CustomerID") where ((string)c.Attribute("CustomerID")).CompareTo("K") > 0 select new XElement("Order", new XElement("CustomerID", (string)c.Attribute("CustomerID")), new XElement("CompanyName", (string)c.Element("CompanyName")), new XElement("ContactName", (string)c.Element("ContactName")), new XElement("EmployeeID", (string)o.Element("EmployeeID")), new XElement("OrderDate", (DateTime)o.Element("OrderDate")) ) ); Console.WriteLine(newCustOrd); } 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c006ea6a-a93c-4c... 请参见 此代码生成以下输出: 复制代码 Attempting to validate, custOrdDoc validated LAZYK Lazy K Kountry Store John Steel 1 1997-03-21T00:00:00 LAZYK Lazy K Kountry Store John Steel 8 1997-05-22T00:00:00 LETSS Let's Stop N Shop Jaime Yorres 1 1997-06-25T00:00:00 LETSS Let's Stop N Shop Jaime Yorres 8 1997-10-27T00:00:00 LETSS Let's Stop N Shop Jaime Yorres 6 1997-11-10T00:00:00 LETSS Let's Stop N Shop Jaime Yorres 4 1998-02-12T00:00:00 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c006ea6a-a93c-4c... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用分组创建层次结构 示例 请参见 发送反馈意见 本示例演示如何将数据分组,再基于分组生成 XML。 示例 请参见 本示例首先按类别对数据分组,再生成新的 XML 文件,其中的 XML 层次结构反映了分组。 本示例使用下面的 XML 文档:示例 XML 文件: 数值数据 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XElement doc = XElement.Load("Data.xml"); var newData = new XElement("Root", from data in doc.Elements("Data") group data by (string)data.Element("Category") into groupedData select new XElement("Group", new XAttribute("ID", groupedData.Key), from g in groupedData select new XElement("Data", g.Element("Quantity"), g.Element("Price") ) ) ); Console.WriteLine(newData); Xml 复制代码 3 24.50 5 4.95 3 66.00 15 29.00 1 89.99 10 .99 8 6.99 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/91a31aeb-f86c-41... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用 XPath 查询 LINQ to XML 示例 请参见 发送反馈意见 本主题介绍一些扩展方法,通过这些扩展方法可以使用 XPath 查询 XML 树。 有关使用这些扩展方法的详细信息, 请参见 System.Xml.XPath.Extensions。 除非有很特别的理由(例如大量使用旧代码)需要使用 XPath 进行查询,否则不建议将 XPath 用于 LINQ to XML。 XPath 查询的执行性能比 LINQ to XML 查询低。 示例 请参见 下面的示例创建一个小型 XML 树,并使用 XPathSelectElements 选择一组元素。 本示例生成以下输出: C# 复制代码 XElement root = new XElement("Root", new XElement("Child1", 1), new XElement("Child1", 2), new XElement("Child1", 3), new XElement("Child2", 4), new XElement("Child2", 5), new XElement("Child2", 6) ); IEnumerable list = root.XPathSelectElements("./Child2"); foreach (XElement el in list) Console.WriteLine(el); 复制代码 4 5 6 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/887e3e5b-964c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 编写 LINQ to XML 轴方法 示例 请参见 发送反馈意见 您可以编写自己的轴方法以便从 XML 树中检索集合。 执行此操作的最佳方式之一是编写可返回元素或属性集合的 扩展方法。 您可以基于应用程序的要求编写扩展方法以返回元素或属性的特定子集。 示例 下面的示例使用两个扩展方法。 第一个扩展方法 GetXPath 在 XObject 上操作并返回一个 XPath 表达式,在 计算该表达式时,将返回节点或属性。 第二个扩展方法 Find 在 XElement 上操作。 它返回 XAttribute 对象 和包含某些指定文本的 XElement 对象的集合。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 public static class MyExtensions { private static string GetQName(XElement xe) { string prefix = xe.GetPrefixOfNamespace(xe.Name.Namespace); if (xe.Name.Namespace == XNamespace.None || prefix == null) return xe.Name.LocalName.ToString(); else return prefix + ":" + xe.Name.LocalName.ToString(); } private static string GetQName(XAttribute xa) { string prefix = xa.Parent.GetPrefixOfNamespace(xa.Name.Namespace); if (xa.Name.Namespace == XNamespace.None || prefix == null) return xa.Name.ToString(); else return prefix + ":" + xa.Name.LocalName; } private static string NameWithPredicate(XElement el) { if (el.Parent != null && el.Parent.Elements(el.Name).Count() != 1) return GetQName(el) + "[" + (el.ElementsBeforeSelf(el.Name).Count() + 1) + "]"; else return GetQName(el); } public static string StrCat(this IEnumerable source, string separator) { return source.Aggregate(new StringBuilder(), (sb, i) => sb .Append(i.ToString()) .Append(separator), s => s.ToString()); } public static string GetXPath(this XObject xobj) { if (xobj.Parent == null) { XDocument doc = xobj as XDocument; if (doc != null) return "."; XElement el = xobj as XElement; if (el != null) return "/" + NameWithPredicate(el); // the XPath data model does not include white space text nodes // that are children of a document, so this method returns null. XText xt = xobj as XText; if (xt != null) return null; XComment com = xobj as XComment; 页码,1/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/71aca03e-96d7-4... if (com != null) return "/" + ( com .Document .Nodes() .OfType() .Count() != 1 ? "comment()[" + (com .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "comment()" ); XProcessingInstruction pi = xobj as XProcessingInstruction; if (pi != null) return "/" + ( pi.Document.Nodes() .OfType() .Count() != 1 ? "processing-instruction()[" + (pi .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "processing-instruction()" ); return null; } else { XElement el = xobj as XElement; if (el != null) { return "/" + el .Ancestors() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + NameWithPredicate(el); } XAttribute at = xobj as XAttribute; if (at != null) return "/" + at .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + "@" + GetQName(at); XComment com = xobj as XComment; if (com != null) return "/" + com .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( com .Parent .Nodes() .OfType() .Count() != 1 ? "comment()[" + (com .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : 页码,2/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/71aca03e-96d7-4... "comment()" ); XCData cd = xobj as XCData; if (cd != null) return "/" + cd .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( cd .Parent .Nodes() .OfType() .Count() != 1 ? "text()[" + (cd .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "text()" ); XText tx = xobj as XText; if (tx != null) return "/" + tx .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( tx .Parent .Nodes() .OfType() .Count() != 1 ? "text()[" + (tx .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "text()" ); XProcessingInstruction pi = xobj as XProcessingInstruction; if (pi != null) return "/" + pi .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( pi .Parent .Nodes() .OfType() .Count() != 1 ? "processing-instruction()[" + (pi .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "processing-instruction()" ); return null; } } public static IEnumerable Find(this XElement source, string value) { if (source.Attributes().Any()) { foreach (XAttribute att in source.Attributes()) { 页码,3/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/71aca03e-96d7-4... 请参见 此代码生成以下输出: string contents = (string)att; if (contents.Contains(value)) yield return att; } } if (source.Elements().Any()) { foreach (XElement child in source.Elements()) foreach (XObject s in child.Find(value)) yield return s; } else { string contents = (string)source; if (contents.Contains(value)) yield return source; } } } class Program { static void Main(string[] args) { XElement purchaseOrders = XElement.Load("PurchaseOrders.xml"); IEnumerable subset = from xobj in purchaseOrders.Find("1999") select xobj; foreach (XObject obj in subset) { Console.WriteLine(obj.GetXPath()); if (obj.GetType() == typeof(XElement)) Console.WriteLine(((XElement)obj).Value); else if (obj.GetType() == typeof(XAttribute)) Console.WriteLine(((XAttribute)obj).Value); } } } 复制代码 /PurchaseOrders/PurchaseOrder[1]/@OrderDate 1999-10-20 /PurchaseOrders/PurchaseOrder[1]/Items/Item[2]/ShipDate 1999-05-21 /PurchaseOrders/PurchaseOrder[2]/@OrderDate 1999-10-22 /PurchaseOrders/PurchaseOrder[3]/@OrderDate 1999-10-22 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/71aca03e-96d7-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 执行文本到 XML 的流式转换 示例 请参见 发送反馈意见 处理文本文件的一种方法是编写使用 yield return 构造一次流式处理一行文本文件的扩展方法。 然后可以编写以 迟缓延迟方式处理文本文件的 LINQ 查询。 如果之后使用 XStreamingElement 对输出进行流式处理,则可以创建 占用最少量内存的从文本文件到 XML 的转换,而不管源文本文件大小如何。 关于流式转换存在一些告诫。 流式转换最适用于可以一次性处理整个文件并且可以按照源文档中的行顺序处理各行 的情况。 如果必须多次处理文件或者必须在处理行之前对行进行排序,则将失去使用流式技术所具有的许多好处。 示例 下面的文本文件 People.txt 是本示例的源文件。 下面的代码包含以延迟方式流式处理文本文件中各行的扩展方法。 本示例生成以下输出: 复制代码 #This is a comment 1,Tai,Yee,Writer 2,Nikolay,Grachev,Programmer 3,David,Wright,Inventor 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 public static class StreamReaderSequence { public static IEnumerable Lines(this StreamReader source) { String line; if (source == null) throw new ArgumentNullException("source"); while ((line = source.ReadLine()) != null) { yield return line; } } } class Program { static void Main(string[] args) { StreamReader sr = new StreamReader("People.txt"); XStreamingElement xmlTree = new XStreamingElement("Root", from line in sr.Lines() let items = line.Split(',') where !line.StartsWith("#") select new XElement("Person", new XAttribute("ID", items[0]), new XElement("First", items[1]), new XElement("Last", items[2]), new XElement("Occupation", items[3]) ) ); Console.WriteLine(xmlTree); sr.Close(); } } Xml 复制代码 Tai Yee Writer 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/202bde50-1571-4... 请参见 Nikolay Grachev Programmer David Wright Inventor 概念 高级查询技术 (LINQ to XML) 参考 XStreamingElement 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/202bde50-1571-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 列出树中的所有节点 示例 请参见 发送反馈意见 有时,列出树中的所有节点会有帮助。 它可帮助准确了解方法或属性是如何影响树的。 以文本形式列出所有节点 的一种方法是生成准确逐一地标识树中所有节点的 XPath 表达式。 使用 LINQ to XML 执行 XPath 表达式不特别有用。 XPath 表达式的性能不如 LINQ to XML 查询,并且 LINQ to XML 查询的功能更强大。 但作为标识 XML 树中节点的一种方式,XPath 可以有效地工作。 示例 本示例演示一个名为 GetXPath 的函数,该函数为 XML 树中的每个节点生成一个特定的 XPath 表达式。 即 使节点位于命名空间中,此示例也可生成相应的 XPath 表达式。 这些 XPath 表达式通过使用命名空间前缀生 成。 然后,示例创建一个包含若干类型节点示例的小型 XML 树。 然后循环访问后代节点并打印每个节点的 XPath 表达式。 您会注意到,XML 声明不是树中的节点。 下面是一个包含若干类型节点的 XML 文件: 下面是上述 XML 树中节点的列表,以 XPath 表达式表示: Xml 复制代码 Text Other Text textBoldTextotherText 复制代码 /processing-instruction() /Root /Root/@AttName /Root/@xmlns:aw /Root/comment() /Root/Child[1] /Root/Child[1]/text() /Root/Child[2] /Root/Child[2]/text() /Root/ChildWithMixedContent /Root/ChildWithMixedContent/text()[1] /Root/ChildWithMixedContent/b /Root/ChildWithMixedContent/b/text() /Root/ChildWithMixedContent/text()[2] /Root/aw:ElementInNamespace /Root/aw:ElementInNamespace/aw:ChildInNamespace C# 复制代码 public static class MyExtensions { private static string GetQName(XElement xe) { string prefix = xe.GetPrefixOfNamespace(xe.Name.Namespace); if (xe.Name.Namespace == XNamespace.None || prefix == null) return xe.Name.LocalName.ToString(); else return prefix + ":" + xe.Name.LocalName.ToString(); } private static string GetQName(XAttribute xa) { string prefix = xa.Parent.GetPrefixOfNamespace(xa.Name.Namespace); if (xa.Name.Namespace == XNamespace.None || prefix == null) return xa.Name.ToString(); 页码,1/5(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/40e27834-70c9-4... else return prefix + ":" + xa.Name.LocalName; } private static string NameWithPredicate(XElement el) { if (el.Parent != null && el.Parent.Elements(el.Name).Count() != 1) return GetQName(el) + "[" + (el.ElementsBeforeSelf(el.Name).Count() + 1) + "]"; else return GetQName(el); } public static string StrCat(this IEnumerable source, string separator) { return source.Aggregate(new StringBuilder(), (sb, i) => sb .Append(i.ToString()) .Append(separator), s => s.ToString()); } public static string GetXPath(this XObject xobj) { if (xobj.Parent == null) { XDocument doc = xobj as XDocument; if (doc != null) return "."; XElement el = xobj as XElement; if (el != null) return "/" + NameWithPredicate(el); // the XPath data model does not include white space text nodes // that are children of a document, so this method returns null. XText xt = xobj as XText; if (xt != null) return null; XComment com = xobj as XComment; if (com != null) return "/" + ( com .Document .Nodes() .OfType() .Count() != 1 ? "comment()[" + (com .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "comment()" ); XProcessingInstruction pi = xobj as XProcessingInstruction; if (pi != null) return "/" + ( pi.Document.Nodes() .OfType() .Count() != 1 ? "processing-instruction()[" + (pi .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "processing-instruction()" ); return null; } else { XElement el = xobj as XElement; if (el != null) { return "/" + 页码,2/5(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/40e27834-70c9-4... el .Ancestors() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + NameWithPredicate(el); } XAttribute at = xobj as XAttribute; if (at != null) return "/" + at .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + "@" + GetQName(at); XComment com = xobj as XComment; if (com != null) return "/" + com .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( com .Parent .Nodes() .OfType() .Count() != 1 ? "comment()[" + (com .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "comment()" ); XCData cd = xobj as XCData; if (cd != null) return "/" + cd .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( cd .Parent .Nodes() .OfType() .Count() != 1 ? "text()[" + (cd .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "text()" ); XText tx = xobj as XText; if (tx != null) return "/" + tx .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( tx .Parent .Nodes() .OfType() .Count() != 1 ? "text()[" + 页码,3/5(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/40e27834-70c9-4... 本示例生成以下输出: (tx .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "text()" ); XProcessingInstruction pi = xobj as XProcessingInstruction; if (pi != null) return "/" + pi .Parent .AncestorsAndSelf() .InDocumentOrder() .Select(e => NameWithPredicate(e)) .StrCat("/") + ( pi .Parent .Nodes() .OfType() .Count() != 1 ? "processing-instruction()[" + (pi .NodesBeforeSelf() .OfType() .Count() + 1) + "]" : "processing-instruction()" ); return null; } } } class Program { static void Main(string[] args) { XNamespace aw = "http://www.adventure-works.com"; XDocument doc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XProcessingInstruction("target", "data"), new XElement("Root", new XAttribute("AttName", "An Attribute"), new XAttribute(XNamespace.Xmlns + "aw", aw.ToString()), new XComment("This is a comment"), new XElement("Child", new XText("Text") ), new XElement("Child", new XText("Other Text") ), new XElement("ChildWithMixedContent", new XText("text"), new XElement("b", "BoldText"), new XText("otherText") ), new XElement(aw + "ElementInNamespace", new XElement(aw + "ChildInNamespace") ) ) ); doc.Save("Test.xml"); Console.WriteLine(File.ReadAllText("Test.xml")); Console.WriteLine("------"); foreach (XObject obj in doc.DescendantNodes()) { Console.WriteLine(obj.GetXPath()); XElement el = obj as XElement; if (el != null) foreach (XAttribute at in el.Attributes()) Console.WriteLine(at.GetXPath()); } } } 复制代码 页码,4/5(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/40e27834-70c9-4... 请参见 Text Other Text textBoldTextotherText ------ /processing-instruction() /Root /Root/@AttName /Root/@xmlns:aw /Root/comment() /Root/Child[1] /Root/Child[1]/text() /Root/Child[2] /Root/Child[2]/text() /Root/ChildWithMixedContent /Root/ChildWithMixedContent/text()[1] /Root/ChildWithMixedContent/b /Root/ChildWithMixedContent/b/text() /Root/ChildWithMixedContent/text()[2] /Root/aw:ElementInNamespace /Root/aw:ElementInNamespace/aw:ChildInNamespace 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,5/5(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/40e27834-70c9-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 从 Office Open XML 文档检索段落 示例 请参见 发送反馈意见 本主题提供一个示例,该示例打开一个 Office Open XML 文档,然后检索文档中所有段落所构成的集合。 有关 Office Open XML 的更多信息,请参见 www.openxmldeveloper.org(可能为英文网页)。 示例 此示例打开一个 Office Open XML 包,使用 Open XML 包中的关系查找文档和样式部件。 然后,它查询文档,投影一个匿名类型的集合,该集合包含段落 XElement 节点、每个段落的样式名称和每个段落的文本。 此示例使用一个名为 StringConcatenate 的扩展方法,示例中也提供了该方法。 有关对示例工作原理进行说明的详细教程,请参见XML 的纯函数转换。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { public static string ParagraphText(XElement e) { XNamespace w = e.Name.Namespace; return e .Elements(w + "r") .Elements(w + "t") .StringConcatenate(element => (string)element); } static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph" && (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/808f6ee0-52bb-4... 请参见 当针对 创建源 Office Open XML 文档 中说明的示例 Open XML 文档运行时,此示例会生成以下输出: // Find all paragraphs in the document. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Retrieve the text of each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = ParagraphText(para.ParagraphNode) }; foreach (var p in paraWithText) Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text); } } 复制代码 StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML< StyleName:Normal >< StyleName:Normal >The following example prints to the console.< StyleName:Normal >< StyleName:Code >using System;< StyleName:Code >< StyleName:Code >class Program {< StyleName:Code > public static void (string[] args) {< StyleName:Code > Console.WriteLine("Hello World");< StyleName:Code > }< StyleName:Code >}< StyleName:Normal >< StyleName:Normal >This example produces the following output:< StyleName:Normal >< StyleName:Code >Hello World< 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/808f6ee0-52bb-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 修改 Office Open XML 文档 示例 请参见 发送反馈意见 本主题显演示一个打开、修改和保存 Office Open XML 文档的示例。 有关 Office Open XML 的更多信息,请参见 www.openxmldeveloper.org(可能为英文网页)。 示例 本示例查找文档中的第一个段落元素。 示例从段落中检索文本,然后删除段落中的所有文本域。 它创建一个由第一个段落已 转换为大写的文本构成的新文本域。 然后将已更改的 XML 序列化为 Open XML 包并关闭该包。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { public static string ParagraphText(XElement e) { XNamespace w = e.Name.Namespace; return e .Elements(w + "r") .Elements(w + "t") .StringConcatenate(element => (string)element); } static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. XDocument xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/244e957c-e037-4... 请参见 如果在运行此程序之后打开 SampleDoc.docx,则可以看到此程序已将文档中的第一个段落转换为大写。 当针对 创建源 Office Open XML 文档 中说明的示例 Open XML 文档运行时,此示例会生成以下输出: // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); PackagePart stylePart = null; XDocument styleDoc = null; if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } XElement paraNode = xDoc .Root .Element(w + "body") .Descendants(w + "p") .FirstOrDefault(); string paraText = ParagraphText(paraNode); // remove all text runs paraNode.Descendants(w + "r").Remove(); paraNode.Add( new XElement(w + "r", new XElement(w + "t", paraText.ToUpper()) ) ); // Save the XML into the package using (XmlWriter xw = XmlWriter.Create(documentPart.GetStream(FileMode.Create, FileAccess.Write))) { xDoc.Save(xw); } Console.WriteLine("New first paragraph: >{0}<", paraText.ToUpper()); } } } } 复制代码 New first paragraph: >PARSING WORDPROCESSINGML WITH LINQ TO XML< 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/244e957c-e037-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 从文件系统填充 XML 树 示例 请参见 发送反馈意见 XML 树有一种有用的常见应用,即作为层次结构名称/值数据存储区。 您可以使用层次结构数据填充 XML 树,然后 对它进行查询、转换和序列化(如有必要)。 在这种用法中,很多 XML 特定的语义(如命名空间和空白行为)都 不重要。 相反,您将 XML 树用作内存中的小型单用户层次结构数据库。 示例 下面的示例使用递归从本地文件系统填充 XML 树。 然后查询该树,计算树中所有文件的总大小。 本示例生成类似下面的输出: C# 复制代码 class Program { static XElement CreateFileSystemXmlTree(string source) { DirectoryInfo di = new DirectoryInfo(source); return new XElement("Dir", new XAttribute("Name", di.Name), from d in Directory.GetDirectories(source) select CreateFileSystemXmlTree(d), from fi in di.GetFiles() select new XElement("File", new XElement("Name", fi.Name), new XElement("Length", fi.Length) ) ); } static void Main(string[] args) { XElement fileSystemTree = CreateFileSystemXmlTree("C:/Tmp"); Console.WriteLine(fileSystemTree); Console.WriteLine("------"); long totalFileSize = (from f in fileSystemTree.Descendants("File") select (long)f.Element("Length")).Sum(); Console.WriteLine("Total File Size:{0}", totalFileSize); } } Xml 复制代码 ConsoleApplication1.exe 4608 ConsoleApplication1.pdb 11776 ConsoleApplication1.vshost.exe 9568 ConsoleApplication1.vshost.exe.manifest 473 ConsoleApplication1.csproj.FileListAbsolute.txt 322 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ba5ae07b-8654-4... 请参见 ConsoleApplication1.exe 4608 ConsoleApplication1.pdb 11776 AssemblyInfo.cs 1454 ConsoleApplication1.csproj 2546 ConsoleApplication1.sln 937 ConsoleApplication1.suo 10752 Program.cs 269 ------ Total File Size:59089 概念 高级查询技术 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ba5ae07b-8654-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 针对 XPath 用户的 LINQ to XML 请参见 发送反馈意见 这组主题演示很多 XPath 表达式及其 LINQ to XML 等效表达式。 所有这些示例都使用 LINQ to XML 中的 XPath 功能,该功能是通过 System.Xml.XPath.Extensions 中的扩展方法实 现的。 这些示例既执行 XPath 表达式也执行 LINQ to XML 表达式。 然后,每个示例都对这两种查询的结果进行比 较,验证 XPath 表达式与 LINQ to XML 查询的功能等效性。 由于这两种类型的查询都从相同的 XML 树返回节点, 因此,查询结果的比较是使用引用标识进行的。 本节内容 主题 说明 XPath 和 LINQ to XML 的比较 概述 XPath 和 LINQ to XML 的异同。 如何: 查找子元素 (XPath- LINQ to XML) 将 XPath 子元素轴与 LINQ to XMLElement 方法进行比较。 关联的 XPath 表达式为:"DeliveryNotes". 如何: 查找子元素列表 (XPath-LINQ to XML) 将 XPath 子元素轴与 LINQ to XMLElements 轴进行比较。 关联的 XPath 表达式为: "./*" 如何: 查找根元素 (XPath- LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 获取根元素。 关联的 XPath 表达式为: "/PurchaseOrders" 如何: 查找后代元素 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 获取具有特定名称的子代元素。 关联的 XPath 表达式为: "//Name" 如何: 根据属性进行筛选 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 获取子代元素,这些子代元素具有 指定的名称,并具有一个带指定值的属性。 关联的 XPath 表达式为: ".//Address[@Type='Shipping']" 如何: 查找相关元素 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 在由其他元素的值所引用的属性上 获取元素选择。 关联的 XPath 表达式为: ".//Customer[@CustomerID=/Root/Orders/Order [12]/CustomerID]" 如何: 查找命名空间中的元 素 (XPath-LINQ to XML) 比较 XPath XmlNamespaceManager 类与 XName 类的 LINQ to XMLNamespace 属性在处理 XML 命名空间时的用法。 关联的 XPath 表达式为: "./aw:*" 如何: 查找前面的同级 (XPath-LINQ to XML) 将 XPath preceding-sibling 轴与 LINQ to XML 子 XNode.ElementsBeforeSelf 轴进行比较。 关联的 XPath 表达式为: "preceding-sibling::*" 如何: 查找子元素的后代 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 获取具有特定名称的子元素的子代 元素。 关联的 XPath 表达式为: "./Paragraph//Text/text()" 如何: 查找两个位置路径的 联合 (XPath-LINQ to XML) 将 XPath 中的联合运算符 | 与 LINQ to XML 中的 Concat(TSource) 标 准查询运算符进行比较。 关联的 XPath 表达式为: "//Category|//Price" 如何: 查找同级节点 比较如何使用 XPath 和 LINQ to XML 查找所有具有特定名称的节点同 页码,1/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/747a0ac9-e55c-4... 请参见 (XPath-LINQ to XML) 级。 关联的 XPath 表达式为: "../Book" 如何: 查找父元素的属性 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 定位到父元素并查找关联的属性。 关联的 XPath 表达式为: "../@id" 如何: 查找具有特定名称的 同级属性 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 查找上下文节点的同级的特定属 性。 关联的 XPath 表达式为: "../Book/@id" 如何: 查找具有特定属性的 元素 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 查找所有包含特定属性的元素。 关联的 XPath 表达式为: "./*[@Select]" 如何: 根据位置查找子元素 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 根据元素的相对位置查找元素。 关联的 XPath 表达式为: "Test[position() >= 2 and position() <= 4]" 如何: 查找前面紧邻的同级 (XPath-LINQ to XML) 比较如何使用 XPath 和 LINQ to XML 查找节点前面紧邻的同级。 关联的 XPath 表达式为: "preceding-sibling::*[1]" 概念 查询 XML 树 使用 XPath 数据模型处理 XML 数据 参考 System.Xml.XPath 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/17ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/747a0ac9-e55c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XPath 和 LINQ to XML 的比较 请参见 发送反馈意见 XPath 和 LINQ to XML 提供一些类似的功能。 二者都可用于查询 XML 树,返回诸如元素集合、属性集合、节点集 合或者元素或属性的值等这样的结果。 但也有一些差异。 XPath 和 LINQ to XML 之间的差异 结果排序 位置谓词 XPath 不允许新类型的投影。 它只能从树中返回节点集合,而 LINQ to XML 可以执行查询并将对象图或 XML 树投影为新形状。LINQ to XML 查询包含更多功能并且比 XPath 表达式的功能强大得多。 XPath 表达式在字符串中孤立存在。 C# 或 Visual Basic 编译器不能帮助在编译时分析 XPath 表达式。 相比 之下,C# 或 Visual Basic 编译器可以分析和编译 LINQ to XML 查询。 编译器能够捕捉许多查询错误。 XPath 结果不是强类型。 在许多情况下,XPath 表达式的计算结果是一个对象,并且该对象需要由开发人员确 定属性类型并根据需要强制转换结果。 相比之下,LINQ to XML 查询生成的投影是强类型。 XPath 1.0 建议规定,作为 XPath 表达式计算结果的集合是无序的。 但在循环访问由 LINQ to XML XPath 轴方法返回的集合时,集合中的节点将按文档顺序返回。 即使是在访问 按反向文档顺序表示谓词的 XPath 轴(如 preceding 和 preceding-sibling)时,情况也如此。 相比之下,多数 LINQ to XML 轴都按文档顺序返回集合,但其中 Ancestors 和 AncestorsAndSelf 两个轴按反 向文档顺序返回集合。 下表枚举各个轴并指示每个轴的集合顺序: LINQ to XML 轴 排序方式 XContainer.DescendantNodes 文档顺序 XContainer.Descendants 文档顺序 XContainer.Elements 文档顺序 XContainer.Nodes 文档顺序 XContainer.NodesAfterSelf 文档顺序 XContainer.NodesBeforeSelf 文档顺序 XElement.AncestorsAndSelf 反向文档顺序 XElement.Attributes 文档顺序 XElement.DescendantNodesAndSelf 文档顺序 XElement.DescendantsAndSelf 文档顺序 XNode.Ancestors 反向文档顺序 XNode.ElementsAfterSelf 文档顺序 XNode.ElementsBeforeSelf 文档顺序 XNode.NodesAfterSelf 文档顺序 XNode.NodesBeforeSelf 文档顺序 在 XPath 表达式中,很多轴的位置谓词都按文档顺序表示,但是反向轴 preceding、preceding-sibling、 ancestor 和 ancestor-or-self 则按反向文档顺序表示。 例如,XPath 表达式 preceding-sibling::* [1] 返回前面紧邻的同级。 即使最终结果集按文档顺序表示,情况也是这样。 相比之下,LINQ to XML 中的所有位置谓词始终按轴顺序表示。 例如,anElement.ElementsBeforeSelf 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/11cb966a-67aa-4... 性能差异 撰写方式的比较 请参见 ().ToList()[0] 返回所查询元素的父级的第一个子元素,而不是前面紧邻的同级。 再例如: anElement.Ancestors().ToList()[0] 返回父元素。 请注意,上面的方法具体化整个集合。 这不是编写该查询的最有效方法。 以这种方式编写查询是为了演示位 置谓词的行为。 编写同一查询的更恰当的方式是使用 First 方法,如下所示: anElement.ElementsBeforeSelf().First(). 如果要查找 LINQ to XML 中的前面紧邻元素,则应编写下面的表达式: ElementsBeforeSelf().Last() 使用 LINQ to XML 中 XPath 功能的 XPath 查询的执行性能比 LINQ to XML 查询低。 LINQ to XML 查询的撰写在某种程度上类似于 XPath 表达式的撰写,但其语法大不相同。 例如,如果名为 customers 的变量中有一个元素,并且您想在所有名为 Customer 的子级元素下查找名为 CompanyName 的孙级元素,则应编写如下所示的 XPath 表达式: 等效的 LINQ to XML 查询是: 每个 XPath 轴都有一些相似处。 C# 复制代码 customers.XPathSelectElements("./Customer/CompanyName"); C# 复制代码 customers.Element("Customer").Elements("CompanyName"); XPath 轴 LINQ to XML 轴 子级(默认轴) XContainer.Elements 父级 (..) XObject.Parent 属性轴 (@) XElement.Attribute 或 XElement.Attributes 上级轴 XNode.Ancestors 上级或自身轴 XElement.AncestorsAndSelf 后代轴 (//) XContainer.Descendants 或 XContainer.DescendantNodes 后代或自身 XElement.DescendantsAndSelf 或 XElement.DescendantNodesAndSelf 后面同级 XNode.ElementsAfterSelf 或 XNode.NodesAfterSelf 前面同级 XNode.ElementsBeforeSelf 或 XNode.NodesBeforeSelf 后面 无直接等效项。 前面 无直接等效项。 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/11cb966a-67aa-4... 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/11cb966a-67aa-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找子元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题将 XPath 子元素轴与 LINQ to XMLElement 方法进行比较。 XPath 表达式为 DeliveryNotes。 示例 请参见 本示例查找子元素 DeliveryNotes。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument cpo = XDocument.Load("PurchaseOrders.xml"); XElement po = cpo.Root.Element("PurchaseOrder"); // LINQ to XML query XElement el1 = po.Element("DeliveryNotes"); // XPath expression XElement el2 = po.XPathSelectElement("DeliveryNotes"); // same as "child::DeliveryNotes" // same as "./DeliveryNotes" if (el1 == el2) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); Console.WriteLine(el1); 复制代码 Results are identical Please leave packages in shed by driveway. 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3e228081-57ee-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找子元素列表 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题将 XPath 子元素轴与 LINQ to XMLElements 轴进行比较。 XPath 表达式为:./* 示例 请参见 本示例查找 Address 元素的所有子元素。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument cpo = XDocument.Load("PurchaseOrders.xml"); XElement po = cpo.Root.Element("PurchaseOrder").Element("Address"); // LINQ to XML query IEnumerable list1 = po.Elements(); // XPath expression IEnumerable list2 = po.XPathSelectElements("./*"); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical Ellen Adams 123 Maple Street Mill Valley CA 10999 USA 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/1a509b76-3fd2-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找根元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何使用 XPath 和 LINQ to XML 获取根元素。 XPath 表达式为: /PurchaseOrders 示例 请参见 此示例查找根元素。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument po = XDocument.Load("PurchaseOrders.xml"); // LINQ to XML query XElement el1 = po.Root; // XPath expression XElement el2 = po.XPathSelectElement("/PurchaseOrders"); if (el1 == el2) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); Console.WriteLine(el1.Name); 复制代码 Results are identical PurchaseOrders 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7d4ec8dd-4197-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找后代元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何获取具有特定名称的后代元素。 XPath 表达式为 //Name。 示例 请参见 本示例查找名为 Name 的所有后代。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument po = XDocument.Load("PurchaseOrders.xml"); // LINQ to XML query IEnumerable list1 = po.Root.Descendants("Name"); // XPath expression IEnumerable list2 = po.XPathSelectElements("//Name"); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical Ellen Adams Tai Yee Cristian Osorio Cristian Osorio Jessica Arnold Jessica Arnold 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/1d965908-d3b4-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 根据属性进行筛选 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何获取具有指定名称、并具有一个带指定值的属性的子代元素。 XPath 表达式为: .//Address[@Type='Shipping'] 示例 请参见 本示例查找名称为 Address,Type、并具有一个带“Shipping”值的属性的子代元素。 本示例使用下面的 XML 文档:示例 XML 文件: 多个采购订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument po = XDocument.Load("PurchaseOrders.xml"); // LINQ to XML query IEnumerable list1 = from el in po.Descendants("Address") where (string)el.Attribute("Type") == "Shipping" select el; // XPath expression IEnumerable list2 = po.XPathSelectElements(".//Address[@Type='Shipping']"); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical
Ellen Adams 123 Maple Street Mill Valley CA 10999 USA
Cristian Osorio 456 Main Street Buffalo NY 98112 USA
Jessica Arnold 4055 Madison Ave Seattle WA 98112 USA
概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/a6f32877-5da0-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找相关元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何在由其他元素的值所引用的属性上获取元素选择。 XPath 表达式为: .//Customer[@CustomerID=/Root/Orders/Order[12]/CustomerID] 示例 本示例查找第 12 个 Order 元素,然后查找该订单的客户。 注意,在 .Net 中,对列表的索引是从零开始的。 在 XPath 谓词中,对节点集合的索引是从 1 开始的。 本 示例反映了这种差别。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument co = XDocument.Load("CustomersOrders.xml"); // LINQ to XML query XElement customer1 = (from el in co.Descendants("Customer") where (string)el.Attribute("CustomerID") == (string)(co .Element("Root") .Element("Orders") .Elements("Order") .ToList()[11] .Element("CustomerID")) select el) .First(); // An alternate way to write the query that avoids creation // of a System.Collections.Generic.List: XElement customer2 = (from el in co.Descendants("Customer") where (string)el.Attribute("CustomerID") == (string)(co .Element("Root") .Element("Orders") .Elements("Order") .Skip(11).First() .Element("CustomerID")) select el) .First(); // XPath expression XElement customer3 = co.XPathSelectElement( ".//Customer[@CustomerID=/Root/Orders/Order[12]/CustomerID]"); if (customer1 == customer2 && customer1 == customer3) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); Console.WriteLine(customer1); 复制代码 Results are identical Hungry Coyote Import Store Yoshi Latimer Sales Representative (503) 555-6874 (503) 555-2376
City Center Plaza 516 Main St.
Elgin OR 97827 USA
页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b5f041a1-895a-4... 请参见
概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b5f041a1-895a-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找命名空间中的元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 XPath 表达式可以在特定命名空间中查找节点。 XPath 表达式使用命名空间前缀来指定命名空间。 若要分析包含命名空间 前缀的 XPath 表达式,必须向实现 IXmlNamespaceResolver 的 XPath 方法传递一个对象。 本示例使用 XmlNamespaceManager。 XPath 表达式为: ./aw:* 示例 请参见 下面的示例读取包含两个命名空间的 XML 树。 它使用 XmlReader 来读取 XML 文档。 然后获取 XmlReader 中的 XmlNameTable 和 XmlNameTable 中的 XmlNamespaceManager。 示例在选择元素时使用 XmlNamespaceManager。 本示例生成以下输出: C# 复制代码 XmlReader reader = XmlReader.Create("ConsolidatedPurchaseOrders.xml"); XElement root = XElement.Load(reader); XmlNameTable nameTable = reader.NameTable; XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable); namespaceManager.AddNamespace("aw", "http://www.adventure-works.com"); IEnumerable list1 = root.XPathSelectElements("./aw:*", namespaceManager); IEnumerable list2 = from el in root.Elements() where el.Name.Namespace == "http://www.adventure-works.com" select el; if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list2) Console.WriteLine(el); 复制代码 Results are identical Chris Preston 123 Main St. Seattle WA 98113 USA Chris Preston 123 Main St. Seattle WA 98113 USA Ship only complete order. Litware Networking Card 1 20.99 Litware 17in LCD Monitor 1 199.99 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5a59e4fc-085a-4d... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找前面的同级 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题将 XPath preceding-sibling 轴与 LINQ to XML 子 XNode.ElementsBeforeSelf 轴进行比较。 XPath 表达式为: preceding-sibling::* 请注意,XPathSelectElements 和 XNode.ElementsBeforeSelf 的结果都遵循文档顺序。 示例 请参见 下面的示例查找 FullAddress 元素,然后使用 preceding-sibling 轴检索前面的元素。 本示例使用下面的 XML 文档:示例 XML 文件: 客户和订单 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XElement co = XElement.Load("CustomersOrders.xml"); XElement add = co.Element("Customers").Element("Customer").Element("FullAddress"); // LINQ to XML query IEnumerable list1 = add.ElementsBeforeSelf(); // XPath expression IEnumerable list2 = add.XPathSelectElements("preceding-sibling::*"); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list2) Console.WriteLine(el); 复制代码 Results are identical Great Lakes Food Market Howard Snyder Marketing Manager (503) 555-7555 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c79b64db-6646-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找子元素的后代 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何获取具有特定名称的子元素的后代元素。 XPath 表达式为: ./Paragraph//Text/text() 示例 请参见 本示例模拟从文字处理文档的 XML 表示形式中提取文本的问题。 示例首先选择所有 Paragraph 元素,然后选择每 个 Paragraph 元素的所有 Text 后代元素。 它不选择 Comment 元素的后代 Text 元素。 本示例生成以下输出: C# 复制代码 XElement root = XElement.Parse( @" This is the start of This comment is not part of the paragraph text. a sentence. This is a second sentence. "); // LINQ to XML query string str1 = root .Elements("Paragraph") .Descendants("Text") .Select(s => s.Value) .Aggregate( new StringBuilder(), (s, i) => s.Append(i), s => s.ToString() ); // XPath expression string str2 = ((IEnumerable)root.XPathEvaluate("./Paragraph//Text/text()")) .Cast() .Select(s => s.Value) .Aggregate( new StringBuilder(), (s, i) => s.Append(i), s => s.ToString() ); if (str1 == str2) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); Console.WriteLine(str2); 复制代码 Results are identical This is the start of a sentence. This is a second sentence. 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/309308ea-3e36-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找两个位置路径的联合 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 使用 XPath 可以查找两个 XPath 位置路径结果的联合。 XPath 表达式为: //Category|//Price 通过使用 Concat(TSource) 标准查询运算符可以获得相同的结果。 示例 请参见 本示例查找所有 Category 元素和所有 Price 元素,并将它们连接到单个集合中。 请注意,LINQ to XML 查 询会调用 InDocumentOrder(T) 以对结果进行排序。 XPath 表达式的计算结果也按文档顺序排列。 本示例使用下面的 XML 文档:示例 XML 文件: 数值数据 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument data = XDocument.Load("Data.xml"); // LINQ to XML query IEnumerable list1 = data .Descendants("Category") .Concat( data .Descendants("Price") ) .InDocumentOrder(); // XPath expression IEnumerable list2 = data.XPathSelectElements("//Category|//Price"); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical A 24.50 B 89.99 A 4.95 A 66.00 B .99 A 29.00 B 6.99 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/4ed5aa06-aa70-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找同级节点 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 您可能需要查找某一节点的具有特定名称的所有同级。 如果上下文节点也具有该特定名称,则生成的集合可能会包括上下 文节点。 XPath 表达式为: ../Book 示例 请参见 本示例首先查找一个 Book 元素,然后查找名为 Book 的所有同级元素。 生成的集合包括上下文节点。 本示例使用下面的 XML 文档:示例 XML 文件: 图书 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument books = XDocument.Load("Books.xml"); XElement book = books .Root .Elements("Book") .Skip(1) .First(); // LINQ to XML query IEnumerable list1 = book .Parent .Elements("Book"); // XPath expression IEnumerable list2 = book.XPathSelectElements("../Book"); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical Garghentini, Davide XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML. Garcia, Debra Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world. 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/4b44ce9f-1aed-4a... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找父元素的属性 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何定位到父元素并查找其属性。 XPath 表达式为: ../@id 示例 请参见 此示例首先查找 Author 元素。 然后,查找父元素的 id 属性。 本示例使用下面的 XML 文档:示例 XML 文件: 图书 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument books = XDocument.Load("Books.xml"); XElement author = books .Root .Element("Book") .Element("Author"); // LINQ to XML query XAttribute att1 = author .Parent .Attribute("id"); // XPath expression XAttribute att2 = ((IEnumerable)author.XPathEvaluate("../@id")).Cast().First(); if (att1 == att2) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); Console.WriteLine(att1); 复制代码 Results are identical id="bk101" 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/797f4a19-6781-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找具有特定名称的同级属性 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 本主题演示如何查找上下文节点的同级的所有属性。 只返回集合中具有特定名称的属性。 XPath 表达式为: ../Book/@id 示例 请参见 本示例首先查找 Book 元素,然后查找名为 Book 的所有同级元素,再查找名为 id 的所有属性。 结果是一 个属性集合。 本示例使用下面的 XML 文档:示例 XML 文件: 图书 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XDocument books = XDocument.Load("Books.xml"); XElement book = books .Root .Element("Book"); // LINQ to XML query IEnumerable list1 = from el in book.Parent.Elements("Book") select el.Attribute("id"); // XPath expression IEnumerable list2 = ((IEnumerable)book.XPathEvaluate("../Book/@id")).Cast(); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XAttribute el in list1) Console.WriteLine(el); 复制代码 Results are identical id="bk101" id="bk102" 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/4bb6a5cd-a294-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找具有特定属性的元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 有时需要查找具有特定属性的所有元素。 您并不关心属性的内容, 而是根据属性是否存在进行选择。 XPath 表达式为: ./*[@Select] 示例 请参见 下面的代码选择具有 Select 属性的所有元素。 本示例生成以下输出: C# 复制代码 XElement doc = XElement.Parse( @" 1 2 3 4 5 "); // LINQ to XML query IEnumerable list1 = from el in doc.Elements() where el.Attribute("Select") != null select el; // XPath expression IEnumerable list2 = ((IEnumerable)doc.XPathEvaluate("./*[@Select]")).Cast(); if (list1.Count() == list2.Count() && list1.Intersect(list2).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical 2 4 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/eabcd7e6-a9d6-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 根据位置查找子元素 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 有时需要根据元素的位置查找元素。 您可能想查找第二个元素,或者查找第三到第五个元素。 XPath 表达式为: Test[position() >= 2 and position() <= 4] 有两种方法以迟缓方式编写此 LINQ to XML 查询。 可以使用 Skip(TSource) 和 Take(TSource) 运算符,也可以使用接受索引 的 Where 重载。 使用 Where 重载时,要使用 lambda 表达式来获得两个参数。 下面的示例演示了根据位置选择的这两种方 法。 示例 请参见 本示例查找第二到第四个 Test 元素。 结果是一个元素集合。 本示例使用下面的 XML 文档:示例 XML 文件: 测试配置 (LINQ to XML)。 本示例生成以下输出: C# 复制代码 XElement testCfg = XElement.Load("TestConfig.xml"); // LINQ to XML query IEnumerable list1 = testCfg .Elements("Test") .Skip(1) .Take(3); // LINQ to XML query IEnumerable list2 = testCfg .Elements("Test") .Where((el, idx) => idx >= 1 && idx <= 3); // XPath expression IEnumerable list3 = testCfg.XPathSelectElements("Test[position() >= 2 and position() <= 4]"); if (list1.Count() == list2.Count() && list1.Count() == list3.Count() && list1.Intersect(list2).Count() == list1.Count() && list1.Intersect(list3).Count() == list1.Count()) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); foreach (XElement el in list1) Console.WriteLine(el); 复制代码 Results are identical Find succeeding characters Examp2.EXE abc def Convert multiple numbers to strings Examp2.EXE /Verbose 123 One Two Three Find correlated key Examp3.EXE a1 b1 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fdd53fd6-11ba-41... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 查找前面紧邻的同级 (XPath-LINQ to XML) 示例 请参见 发送反馈意见 有时,您需要查找某一节点的前面紧邻同级。 由于与 LINQ to XML 相比,XPath 中前面紧邻轴的位置谓词的语义 同,因此这是一个更值得关注的比较。 示例 请参见 在本示例中,LINQ to XML 查询使用 Last 运算符查找由 ElementsBeforeSelf 返回的集合中的最后一个节点。 相比之下,XPath 表达式使用值为 1 的谓词来查找前面紧邻的元素。 本示例生成以下输出: C# 复制代码 XElement root = XElement.Parse( @" "); XElement child4 = root.Element("Child4"); // LINQ to XML query XElement el1 = child4 .ElementsBeforeSelf() .Last(); // XPath expression XElement el2 = ((IEnumerable)child4 .XPathEvaluate("preceding-sibling::*[1]")) .Cast() .First(); if (el1 == el2) Console.WriteLine("Results are identical"); else Console.WriteLine("Results differ"); Console.WriteLine(el1); 复制代码 Results are identical 概念 针对 XPath 用户的 LINQ to XML 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/1bb10740-0f2b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XML 的纯函数转换 请参见 发送反馈意见 本节提供 XML 的函数转换教程。 它包括使用函数转换所必须理解的主要概念和语言构造的说明以及使用函数转换 来操作 XML 文档的示例。 虽然本教程提供 LINQ to XML 代码示例,但所有基础概念也适用于其他 LINQ 技术。 在 Word 文档中操作信息教程提供一系列示例,每个示例都是在前一个示例的基础上生成的。 这些示例演示用于操 作 XML 的纯函数转换方法。 本教程假定您已了解 C# 或 Visual Basic 的使用知识,而解释 C# 3.0 和 Visual Basic 9.0 新增的相关概念和语法。 本教程不提供语言构造的详细语义,但提供到相应语言文档的链接。 还假定您已了解基本计算机科学概念和 XML(包括 XML 命名空间)的使用知识。 本节内容 请参见 主题 说明 纯函数转换简介 说明函数转换并定义相关术语。 教程: 将查询链接在一起 详细说明迟缓计算和延迟执行。 教程: 操作 WordprocessingML 文档中的内容 演示函数转换的教程。 概念 Language-Integrated Query 查询 XML 树 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/10f44635-d906-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 纯函数转换简介 请参见 发送反馈意见 本节介绍函数转换,包括基本概念和支持的语言构造。 本节对面向对象的编程方法与函数转换编程方法进行了对 比,并针对如何转换到后者提供了一些建议。 尽管可以在很多编程方案中都使用函数转换,但此处使用 XML 转换 作为具体示例。 本节内容 请参见 主题 说明 概念和术语(函数转换) 介绍纯函数转换的概念和术语。 函数编程与命令性编程 将函数编程与更传统的命令性(过程)编程进行对比。 重构为纯函数 介绍纯函数,并提供了纯函数和非纯函数的示例。 函数转换的适用性 描述函数转换的典型应用场景。 XML 的函数转换 描述在转换 XML 树的上下文中的函数转换。 概念 XML 的纯函数转换 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/ef0ea822-39db-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 概念和术语(函数转换) 请参见 发送反馈意见 本主题介绍纯函数转换的概念和术语。 与更加传统的命令性编程方式相比,用来转换数据的函数转换方法所生成的 代码往往编程速度更快、含义更明确、更易于调试和维护。 请注意,本节中的主题并不是要透彻解释函数编程。 这些主题只是介绍函数编程的一些功能,这些功能降低了将 XML 从一种形状转换为另一种形状的难度。 什么是纯函数转换? 术语 在纯函数转换中,称为“纯函数”的一组函数定义如何将一组结构化数据从其原始格式转换为另一种格式。 “纯”表示这些函数是“可组合的”,这要求这些函数具有以下特点: z 独立的,这样函数就可以自由排序和重新排列,而不会与程序的其他部分相互牵连和依赖。 纯转换不了 解其环境,对环境也没有任何影响。 也就是说,用在转换中的函数没有负作用。 z 无状态的,因而对相同输入执行相同的函数或特定的一组函数将始终产生相同的输出。 纯转换对于先前 对它的使用没有记忆。 函数编程 函数编程是一种编程方法,直接支持纯函数转换。 学术团体对诸如 ML、Scheme、Haskell 和 F#(由 Microsoft Research 开发)等通用函数编程语言一直抱有 浓厚的兴趣。 虽然 C# 和 Visual Basic 一直可以用来编写纯函数转换,然而实际操作的困难使之对大多数程 序员来说不具吸引力。 但是有了 C# 3.0 和 Visual Basic 9.0,像 lambda 表达式和类型推理这样的新语言构 造大大降低了函数编程的难度,也提高了函数编程的效率。 有关函数编程的更多信息,请参见函数编程与命令性编程。 特定于域的 FP 语言 虽然通用函数编程语言并未得到广泛采用,但特定于域的函数编程语言却得到了较为成功的应用。 例如,级 联样式表 (CSS) 用于确定许多网页的外观,可扩展样式表转换语言 (XSLT) 样式表广泛应用于 XML 数据操作 中。 有关 XSLT 的更多信息,请参见 XSLT 转换。 重要说明: 在本教程的其余部分,术语“纯函数”用于概括表示编程方法,而不是具体的语言功能。 请注意,纯函数在 C# 中必须实现为方法,在 Visual Basic 中必须实现为函数。 (在 Visual Basic 中,子 例程无法返回值,因此不能使用它们编写纯函数转换。) 此外,不要将纯函数与 C++ 中的纯虚方法混淆。 后者表示包含类是抽象的,并且不提供方法体。 下表定义了一些与函数转换相关的术语。 高阶(第一类)函数 可作为编程对象的一种函数。 例如,高阶函数可以传递到其他函数或从其他函数返回。 在 C# 和 Visual Basic 中,委托和 lambda 表达式是支持高阶函数的语言功能。 若要编写高阶函数,需要声明一个或多个参 数来接受委托,而在调用该函数时通常使用 lambda 表达式。 许多标准查询运算符都属于高阶函数。 有关更多信息,请参见Standard Query Operators Overview。 lambda 表达式 实质上是一个匿名的内联函数,可用在任何需要委托类型的地方。 这是对 lambda 表达式的一个简化的定 义,但足以满足本教程的需要。 有关更多信息,请参见 Lambda Expressions [C#] 或 Lambda Expressions [Visual Basic]。 集合 结构化的一组数据,通常具有统一的类型。 若要与 LINQ 兼容,集合必须实现 IEnumerable 接口或 IQueryable 接口(或它们对应的泛型接口 IEnumerator(T) 或 IQueryable(T) 之一)。 元组(匿名类型) 一个数学概念,元组是一个有限的对象序列,每个对象具有特定的类型。 元组也称为有序列表。 匿名类型 是此概念的语言实现,支持在声明未命名类类型的同时实例化该类型的对象。 有关更多信息,请参见Anonymous Types [C#] 或Anonymous Types。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fc66efe7-51b2-4e... 请参见 类型推理(隐式确定类型) 在没有显式类型声明的情况下编译器确定变量类型的能力。 有关更多信息,请参见Implicitly Typed Local Variables [C#] 或Local Type Inference [Visual Basic]。 延迟执行和迟缓计算 延迟表达式的计算,直到实际需要它的求解值。 集合中支持延迟执行。 有关更多信息,请参见 The Three Parts of a LINQ Query和LINQ to XML 中的延迟执行和迟缓计算。 本节各处的代码示例中将使用这些语言功能。 概念 纯函数转换简介 函数编程与命令性编程 The Three Parts of a LINQ Query LINQ to XML 中的延迟执行和迟缓计算 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fc66efe7-51b2-4e... 全部折叠 代码:C# 语言集成查询 (LINQ) 函数编程与命令性编程 请参见 发送反馈意见 本主题对函数编程和更传统的命令性(过程性)编程进行比较。 函数编程与命令性编程 使用 XSLT 的函数编程 纯函数的优点 针对 OOP 开发人员的转换 为支持使用纯函数方法解决问题,特此创建了函数编程范例。 函数编程是一种声明性编程形式。 相比之下, 大多数主流语言,包括面向对象的编程 (OOP) 语言(如 C#、Visual Basic、C++ 和 Java –)主要都是为支持 命令性(过程性)编程而设计的。 使用命令性方法时,开发人员编写的代码应严格细致地说明计算机为完成目标而必须执行的步骤。 这有时称 为算法编程。 相比之下,函数方法涉及将问题编成一组要执行的函数。 您要仔细地定义每个函数的输入值和 每个函数的返回值。 下表说明了这两种方法之间的一些总体差别。 虽然多数语言旨在支持特定编程范例,但许多通用语言具有很高的灵活性,能够支持多个范例。 例如,包含 函数指针的多数语言都可用于可靠地支持函数编程。 而且,在 C# 3.0 和 Visual Basic 9.0 中,已添加了显式 语言扩展以支持函数编程,包括 lambda 表达式和类型推理。 LINQ 技术是一种声明性函数编程形式。 特征 命令性方法 函数方法 程序员关注点 如何执行任务(算法)和如何跟踪状态更改。 需要哪些信息和需要进行什么转换。 状态更改 重要。 不存在。 执行顺序 重要。 不太重要。 主要流控制 循环、条件和函数(方法)调用。 函数调用,包括递归。 主要操作单元 结构或类的实例。 作为第一类对象和数据集合的函。 很多 XSLT 开发人员熟悉纯函数方法。 开发 XSLT 样式表的最有效方式是将每个模板视为一个独立的、可组 合的转换。 执行顺序完全不重要。 XSLT 不允许副作用(但用于执行过程代码的转义机制除外,它可能引入 可导致功能不纯的副作用)。 不过,虽然 XSLT 是一个有效的工具,但其某些特性并不是最佳的。 例如,在 XML 中表示编程构造会使代码相对繁琐,因此难于维护。 而且,流控制对递归的依赖性很强,因此会使代码 的可读性较差。 有关 XSLT 的更多信息,请参见 XSLT 转换。 但 XSLT 在使用纯函数方法将 XML 从一种形状转换为另一种形状方面确有其自身的价值。 使用 LINQ to XML 的纯函数编程在许多方面与 XSLT 类似。 但 LINQ to XML、C# 3.0 和 Visual Basic 9.0 中引入的编程构造允 许您编写比 XSLT 更具可读性且更容易维护的纯函数转换。 以纯函数形式实现函数转换的主要原因是纯函数是可以组合的: 即独立并且无状态。 这些特性可带来很多好 处,包括以下各项: z 可读性和可维护性更高。 这是因为每个函数都设计为在给定参数的情况下完成特定任务。 函数不依赖于 任何外部状态。 z 更易于反复开发。 因为代码更容易重构,因此对设计的更改通常更容易实现。 例如,假设您编写了一个 复杂的转换,但随后发现某些代码在转换中重复多次。 如果您通过纯方法重构,则可以随意调用纯方法 而不必担心会有什么副作用。 z 更易于测试和调试。 由于纯函数更容易单独测试,因此您可以编写使用典型值、有效边缘情况和无效边 缘情况调用纯函数的测试代码。 在传统的面向对象编程 (OOP) 中,大多数开发人员都习惯于命令性/过程式编程。 若要切换到纯函数式开 发,开发人员必须转变开发思路和方法。 为了解决问题,OOP 开发人员需要设计类层次结构,注意进行恰当的封装并按照类约定进行思考。 对象类型 的行为和状态至为重要,并提供语言功能(如类、接口、继承和多态性)以解除这些问题。 相比之下,函数编程将计算机的操作问题处理为数据集合的纯函数转换计算。 函数编程可以避免使用状态和 可变数据,并改为强调函数的应用。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/446be006-d63d-4... 请参见 但 C# 和 Visual Basic 并不要求完全转变为函数编程,因为它们同时支持命令性编程和函数编程。 开发人员 可以选择哪种方法最适合特定方案。 实际上,程序通常组合使用这两种方法。 概念 纯函数转换简介 XSLT 转换 重构为纯函数 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/446be006-d63d-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 重构为纯函数 请参见 发送反馈意见 纯函数转换的一个重要方面是学习如何使用纯函数重构代码。 如本节前面所述,纯函数具有两个有用的特性: z 它没有任何副作用。 函数不会更改函数以外的任何变量或任何类型的数据。 z 它具有一致性。 在提供同一组输入数据的情况下,它将始终返回相同的输出值。 转换为函数编程的一种方式是重构现有代码以消除不必要的副作用和外部依赖项。 这样,您可以创建现有代码的纯 函数版本。 本主题讨论什么是纯函数,什么不是纯函数。 在 WordprocessingML 文档中操作信息教程演示如何操作 WordprocessingML 文档并包括两个有关如何使用纯函数进行重构的示例。 消除副作用和外部依赖项 注意: 函数编程中的常用术语是使用纯函数重构程序。 在 Visual Basic 和 C++ 中,这对应于使用相应语言的函数。 但在 C# 中,函数称为方法。 出于本文论述的需要,在 C# 中以方法的形式实现了一个纯函数。 下面的示例对比两个非纯函数和一个纯函数。 可更改类成员的非纯函数 在下面的代码中,HypenatedConcat 函数不是纯函数,因为它修改了类中的 aMember 数据成员: 此代码生成以下输出: 请注意,它与修改的数据是具有 public 还是 private 访问或者是 static (shared) 成员还是实例成员均无 关。 纯函数不会更改函数以外的任何数据。 可更改参数的非纯函数 此外,此同一个函数的下面版本不是纯函数,因为它修改了其参数 sb 的内容。 C# 复制代码 public class Program { private static string aMember = "StringOne"; public static void HypenatedConcat(string appendStr) { aMember += '-' + appendStr; } public static void Main() { HypenatedConcat("StringTwo"); Console.WriteLine(aMember); } } 复制代码 StringOne-StringTwo C# 复制代码 public class Program { public static void HypenatedConcat(StringBuilder sb, String appendStr) { sb.Append('-' + appendStr); } public static void Main() { StringBuilder sb1 = new StringBuilder("StringOne"); HypenatedConcat(sb1, "StringTwo"); Console.WriteLine(sb1); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3beb60fe-3b22-4... 标准查询运算符 请参见 此版本的程序生成的输出与第一个版本相同,因为 HypenatedConcat 函数已通过调用 Append 成员函数而更 改了其第一个参数的值(状态)。 请注意,即使 HypenatedConcat 实际上使用了按值调用参数传递,也会 发生此更改。 纯函数 下面版本的程序演示如何将 HypenatedConcat 函数作为纯函数实现。 此版本同样生成相同的输出行: StringOne-StringTwo. 请注意保留串联值,它存储在中间变量 s2 中。 一种非常实用的方法是编写本地不纯(即声明和修改本地变量)但在全局为纯的函数。 这些函数具有许多需 要的可组合特性,但舍弃了一些较为繁复的函数编程语法,如在使用简单的循环即可完成同样操作时必须使用 递归。 } } 重要说明: 对于引用类型,按值传递参数会得到对所传递对象的引用的副本。 此副本与原始引用一样,仍与同一个实 例数据关联(除非为引用变量分配新对象)。 函数修改参数不一定需要按引用调用。 C# 复制代码 class Program { public static string HyphenatedConcat(string s, string appendStr) { return (s + '-' + appendStr); } public static void Main(string[] args) { string s1 = "StringOne"; string s2 = HyphenatedConcat(s1, "StringTwo"); Console.WriteLine(s2); } } 标准查询运算符的重要特性是它们以纯函数的形式实现。 有关更多信息,请参见Standard Query Operators Overview。 概念 纯函数转换简介 函数编程与命令性编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3beb60fe-3b22-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 函数转换的适用性 请参见 发送反馈意见 纯函数转换适用于多种情况。 函数转换方法最适合查询和操作结构化数据,因此它非常适合 LINQ 技术。 但函数转换比使用 LINQ 具有更广泛的 适用性。 任何侧重于将数据从一种形式转换为另一种形式的进程都可以考虑作为函数转换的候选项。 此方法适用于乍看可能不是候选项的许多问题。 在独立或与 LINQ 一起使用时,应考虑对以下方面使用函数转换: z 基于 XML 的文档。 使用任何 XML 方言的格式良好的数据均可以通过函数转换容易地操作。 有关更多信息, 请参见 XML 的函数转换。 z 其他结构化文件格式。 从 Windows.ini 文件到纯文本文档,多数文件都有适于本身进行分析和转换的结构。 z 数据流协议。 将数据编码为通信协议和从通信协议解码数据通常可以由简单的函数转换来表示。 z RDBMS 和 OODBMS 数据。 关系数据库和面向对象的数据库和 XML 一样,是广泛使用的结构化数据源。 z 数学、统计和科学解决方案。 这些字段适用于操作大型数据集,以帮助用户处理可视化、评估或实际解决重要 问题。 如重构为纯函数中所述,使用纯函数是函数编程的一个示例。 除了直接好处外,使用纯函数还可对从函数转换角度 考虑问题提供宝贵的经验。 这种方法也可能对程序和类设计产生重要影响。 当问题本身适于数据转换解决方案 (如上所述)时更是如此。 虽然受函数转换透视影响的设计不在本教程的范围内,但在以进程为中心作为操作者和以对象为中心作为操作者之 间,他们更倾向于前者,生成的解决方案倾向于以一系列大规模转换而不是单独的对象状态更改的形式实现。 此外请记住,C# 和 Visual Basic 同时支持命令性方法和函数方法,因此应用程序的最佳设计可能需要合并使用这两 个元素。 请参见 概念 纯函数转换简介 XML 的函数转换 重构为纯函数 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/46cfda38-25b7-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XML 的函数转换 请参见 发送反馈意见 本主题讨论用于修改 XML 文档的纯函数转换方法,并将该方法与过程方法进行比较。 修改 XML 文档 请参见 对 XML 程序员来说,最常见的任务之一就是将 XML 从一种形状转换为另一种形状。 XML 文档的形状就是文 档的结构,包括下列内容: z 文档所表达的层次结构。 z 元素和属性的名称。 z 元素和属性的数据类型。 通常,将 XML 从一种形状转换为另一种形状的最有效方法是纯函数转换方法。 在这种方法中,程序员的主要 任务是创建一个转换,该转换将应用到整个 XML 文档(或应用到一个或多个严格定义的节点)。 可以说,函 数转换最容易进行编码(如果程序员熟悉这种方法),生成的代码最容易维护,并且相比其他方法通常更加简 洁。 XML 函数转换技术 Microsoft 提供了两种函数转换技术用于 XML 文档: XSLT 和 LINQ to XML。 在 System.Xml.Xsl 托管命名空 间和 MSXML 的本机 COM 实现中都支持 XSLT。 尽管 XSLT 是操作 XML 文档的可靠技术,但它要求专门领 域的专业知识,即 XSLT 语言和支持它的 API。 LINQ to XML 提供了必要的工具,使用这些工具可以在 C# 或 Visual Basic 代码中以富于表现力而又强有力的 方式编写纯函数转换。 例如,LINQ to XML 文档中的很多示例都使用纯函数方法。 此外,在在 WordprocessingML 文档中处理信息教程中,我们在函数方法中使用 LINQ to XML 处理 Microsoft Word 文档中 的信息。 有关 LINQ to XML 与其他 Microsoft XML 技术的更全面比较,请参见 LINQ to XML 与其他 XML 技术。 如果源文档具有不规则的结构,则推荐使用 XSLT 工具进行以文档为中心的转换。 但是 LINQ to XML 也可以 执行以文档为中心的转换。 有关更多信息,请参见如何: 使用批注转换 XSLT 样式中的 LINQ to XML 树。 概念 纯函数转换简介 教程: 操作 WordprocessingML 文档中的内容 LINQ to XML 与其他 XML 技术 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e8764201-e59d-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 教程: 将查询链接在一起 请参见 发送反馈意见 本教程阐释将查询链接在一起时的处理模型。 将查询链接在一起是编写函数转换的一个关键部分。 确切理解链接 的查询如何工作非常重要。 处理 Office Open XML 文档的查询广泛使用了这种方法。 本节内容 请参见 主题 说明 LINQ to XML 中的延迟执行和迟缓计算 描述延迟执行和迟缓计算的概念。 延迟执行示例 提供一个延迟执行的示例。 链接查询示例 演示在将查询链接在一起时延迟执行的工作方式。 中间具体化 确定和说明中间具体化的语义。 将标准查询运算符链接在一起 描述标准查询运算符的迟缓语义。 概念 XML 的纯函数转换 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c08d228a-f07a-4c... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 中的延迟执行和迟缓计算 请参见 发送反馈意见 实现查询和轴操作通常是为了使用延迟执行。 本主题解释延迟执行的要求和优点,以及某些实现注意事项。 延迟执行 积极与迟缓计算 后续步骤 请参见 延迟执行意味着表达式的计算延迟,直到真正需要它的实现值为止。 当必须操作大型数据集合,特别是在包 含一系列链接的查询或操作的程序中操作时,延迟执行可以大大改善性能。 在最佳情况下,延迟执行只允许 对源集合的单个循环访问。 LINQ 技术广泛应用了延迟执行,包括在核心 System.Linq 类的成员和不同 LINQ 命名空间中的扩展方法(如 System.Xml.Linq.Extensions)中使用。 当在迭代器块内使用时,C# 语言可以通过 yield 关键字(以 yield-return 语句形式),直接支持延迟执行。 此类迭代器必须返回类型为 IEnumerator 或 IEnumerator(T)(或派生类型)的集合。 在 Visual Basic 9.0 中,没有与 yield 对等的关键字,因此在这种语言中,实现延迟执行更加复杂。 当您编写实现延迟执行的方法时,还必须确定是使用迟缓计算还是积极计算来实现该方法。 z 在迟缓计算中,在每次调用迭代器时,都处理源集合的一个元素。 这是实现迭代器的典型方式。 z 在积极计算中,在第一次调用迭代器时,就会对整个集合进行处理。 还可能需要源集合的临时副本。 例 如,OrderBy 方法必须在返回第一个元素前对整个集合进行排序。 迟缓计算通常产生更好的性能,因为它将系统开销处理平均分配到整个集合的计算中,并将临时数据的使用降 至最低。 当然,对于某些操作,除了具体化中间结果之外,再没有其他选择。 本教程的下一个主题将解释延迟执行: z 延迟执行示例 概念 教程: 将查询链接在一起 概念和术语(函数转换) Aggregation Operations yield [C# keyword] 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/96bd8bb8-27db-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 延迟执行示例 请参见 发送反馈意见 本主题演示延迟执行和迟缓计算如何影响 LINQ to XML 查询的执行。 示例 后续步骤 请参见 下面的示例演示使用采用延迟执行的扩展方法时的执行顺序。 此示例声明一个由三个字符串组成的数组。 然 后,循环访问 ConvertCollectionToUpperCase 所返回的集合。 本示例生成以下输出: 请注意,在循环访问 ConvertCollectionToUpperCase 所返回的集合时,每一项都从源字符串数组检索,并 且在源字符串数组中检索下一项之前,被转换为大写形式。 可以看到,在 Main 的 foreach 循环中处理所返回集合的每一项之后,字符串数组才会完全转换为大写形 式。 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 public static class LocalExtensions { public static IEnumerable ConvertCollectionToUpperCase(this IEnumerable source) { foreach (string str in source) { Console.WriteLine("ToUpper: source {0}", str); yield return str.ToUpper(); } } } class Program { static void Main(string[] args) { string[] stringArray = { "abc", "def", "ghi" }; var q = from str in stringArray.ConvertCollectionToUpperCase() select str; foreach (string str in q) Console.WriteLine("Main: str {0}", str); } } 复制代码 ToUpper: source abc Main: str ABC ToUpper: source def Main: str DEF ToUpper: source ghi Main: str GHI 本教程下一主题演示如何将查询链接到一起: z 链接查询示例 概念 教程: 将查询链接在一起 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/76c0cb39-1633-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 链接查询示例 请参见 发送反馈意见 此示例建立在前一示例的基础上,演示两个都使用延迟执行和迟缓计算的查询链接到一起时会发生什么情况。 示例 在此示例中,还会引入另一个扩展方法 AppendString,该方法将一个指定字符串追加到源集合的每个字符串 上,然后生成新的字符串。 本示例生成以下输出: 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 public static class LocalExtensions { public static IEnumerable ConvertCollectionToUpperCase(this IEnumerable source) { foreach (string str in source) { Console.WriteLine("ToUpper: source >{0}<", str); yield return str.ToUpper(); } } public static IEnumerable AppendString(this IEnumerable source, string stringToAppend) { foreach (string str in source) { Console.WriteLine("AppendString: source >{0}<", str); yield return str + stringToAppend; } } } class Program { static void Main(string[] args) { string[] stringArray = { "abc", "def", "ghi" }; IEnumerable q1 = from s in stringArray.ConvertCollectionToUpperCase() select s; IEnumerable q2 = from s in q1.AppendString("!!!") select s; foreach (string str in q2) { Console.WriteLine("Main: str >{0}<", str); Console.WriteLine(); } } } 复制代码 ToUpper: source >abc< AppendString: source >ABC< Main: str >ABC!!!< ToUpper: source >def< AppendString: source >DEF< Main: str >DEF!!!< ToUpper: source >ghi< AppendString: source >GHI< Main: str >GHI!!!< 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7b5e9db1-e247-4... 后续步骤 请参见 在此示例中,可以看到,每个扩展方法每次只对源集合中的一个项进行一次操作。 在此示例中,可以清楚地看到,尽管已将生成集合的查询链接到了一起,但未具体化任何中间集合。 相反, 每一项只是从一个迟缓方法传递到下一个迟缓方法。 这种方法的内存需求量比另一种方法小得多,在另一种 方法中,首先获取一个字符串数组,然后创建第二个字符串数组(其中的字符串都已转换为大写形式),最后 创建第三个字符串数组(其中的每个字符串都追加了感叹号)。 本教程下一主题演示中间具体化: z 中间具体化 概念 教程: 将查询链接在一起 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7b5e9db1-e247-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 中间具体化 请参见 发送反馈意见 有时,稍不小心就会导致查询中的集合过早具体化,从而显著改变应用程序的内存和性能配置文件。 有些标准查询 运算符会在生成单个元素之前导致其源集合具体化。 例如,Enumerable.OrderBy 首先循环访问其整个源集合,然后 对所有项排序,最后生成第一项。 这意味着获取排序集合中的第一项需要高开销;其后的每一项不需要高开销。 这样做很有意义:该查询运算符将不可能以其他方式操作。 示例 本示例更改上一示例。 AppendString 方法在循环访问源之前调用 ToList(TSource)。 这将导致具体化。 本示例生成以下输出: 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 public static class LocalExtensions { public static IEnumerable ConvertCollectionToUpperCase(this IEnumerable source) { foreach (string str in source) { Console.WriteLine("ToUpper: source >{0}<", str); yield return str.ToUpper(); } } public static IEnumerable AppendString(this IEnumerable source, string stringToAppend) { // the following statement materializes the source collection in a List // before iterating through it foreach (string str in source.ToList()) { Console.WriteLine("AppendString: source >{0}<", str); yield return str + stringToAppend; } } } class Program { static void Main(string[] args) { string[] stringArray = { "abc", "def", "ghi" }; IEnumerable q1 = from s in stringArray.ConvertCollectionToUpperCase() select s; IEnumerable q2 = from s in q1.AppendString("!!!") select s; foreach (string str in q2) { Console.WriteLine("Main: str >{0}<", str); Console.WriteLine(); } } } 复制代码 ToUpper: source >abc< ToUpper: source >def< ToUpper: source >ghi< AppendString: source >ABC< Main: str >ABC!!!< AppendString: source >DEF< 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/60f787eb-0e55-4... 后续步骤 请参见 在此示例中,您可以看到,对 ToList(TSource) 的调用会导致 AppendString 生成第一项之前枚举其整个源。 如果源是一个大数组,这将显著改变应用程序的内存配置文件。 Main: str >DEF!!!< AppendString: source >GHI< Main: str >GHI!!!< 标准查询运算符也可以链接在一起。 本教程的最后一个主题将对此进行说明。 z 将标准查询运算符链接在一起 概念 教程: 将查询链接在一起 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/60f787eb-0e55-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 将标准查询运算符链接在一起 请参见 发送反馈意见 这是将查询链接在一起教程中的最后一个主题。 标准查询运算符也可以链接在一起。 例如,您可以插入 Enumerable.Where 运算符,并且该运算符也可以以迟缓方 式操作。 它不会具体化任何中间结果。 示例 在此示例中,调用 ConvertCollectionToUpperCase 之前将调用 Where 方法。 Where 方法的操作方式与本 教程前面示例中使用的迟缓方法 ConvertCollectionToUpperCase 和 AppendString 几乎完全相同。 区别之一是在本例中,Where 方法循环访问其源集合,确定第一项不传递谓词,然后获取传递谓词的下一项。 示例然后生成第二项。 但基本要旨是相同的:除非必要,否则不具体化中间集合。 在使用查询表达式时,会将查询表达式转换为对标准查询运算符的调用,这一原理同样适用。 本节中查询 Office Open XML 文档的所有示例都使用相同的原理。 延迟执行和迟缓计算是有效使用 LINQ(和 LINQ to XML)所必须理解的一些基础概念。 本示例生成以下输出: 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 public static class LocalExtensions { public static IEnumerable ConvertCollectionToUpperCase(this IEnumerable source) { foreach (string str in source) { Console.WriteLine("ToUpper: source >{0}<", str); yield return str.ToUpper(); } } public static IEnumerable AppendString(this IEnumerable source, string stringToAppend) { foreach (string str in source) { Console.WriteLine("AppendString: source >{0}<", str); yield return str + stringToAppend; } } } class Program { static void Main(string[] args) { string[] stringArray = { "abc", "def", "ghi" }; IEnumerable q1 = from s in stringArray.ConvertCollectionToUpperCase() where s.CompareTo("D") >= 0 select s; IEnumerable q2 = from s in q1.AppendString("!!!") select s; foreach (string str in q2) { Console.WriteLine("Main: str >{0}<", str); Console.WriteLine(); } } } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/199dd5f5-a3c9-4... 请参见 复制代码 ToUpper: source >abc< ToUpper: source >def< AppendString: source >DEF< Main: str >DEF!!!< ToUpper: source >ghi< AppendString: source >GHI< Main: str >GHI!!!< 概念 教程: 将查询链接在一起 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/199dd5f5-a3c9-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 教程: 操作 WordprocessingML 文档中的内容 请参见 发送反馈意见 本教程演示如何应用函数转换方法和 LINQ to XML 来操作 XML 文档。 C# 和 Visual Basic 示例查询并操作用 Microsoft Word 保存的 Office Open XML WordprocessingML 文档中的信息。 有关更多信息,请参见 OpenXML Developer(OpenXML 开发人员)网站。 本节内容 请参见 主题 说明 WordprocessingML 文档的形状 提供有关 WordprocessingML 文档详细信息的简要说 明。 创建源 Office Open XML 文档 为创建用于本教程中的查询的源文档提供分步说明。 查找默认段落样式 演示用于查找文档默认样式名称的查询。 检索段落及其样式 演示用于检索文档段落集合的查询。 检索段落的文本 补充前一个查询以检索每个段落的文本。 使用扩展方法进行重构 通过使用扩展方法进行重构来简化代码。 使用纯函数进行重构 通过使用纯函数进行重构来进一步简化代码。 对不同形状的 XML 进行投影 通过将 XML 投影为不同于原始文档的形状来完成 XML 转换。 在 Word 文档中查找文本 使用前面的查询在文档中查找指定的文本字符串。 Office Open XML WordprocessingML 文档的详 细信息 提供 Office Open XML WordprocessingML 文档的一些详 细信息。 概念 XML 的纯函数转换 纯函数转换简介 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/2696355e-4f83-4... 全部折叠 代码:C# 语言集成查询 (LINQ) WordprocessingML 文档的形状 请参见 发送反馈意见 本主题介绍 WordprocessingML 文档的 XML 形状。 Microsoft Office 格式 WordprocessingML 文档的形状 2007 Microsoft Office 系统的本机文件格式为 Office Open XML(通常称为 Open XML)。 Open XML 是一种基于 XML 的格式(Ecma 标准),当前即将通过 ISO-IEC 标准流程。 Open XML 中用于文字处理文件的标记语言称为 WordprocessingML。 本教程使用 WordprocessingML 源文件作为示例的输入。 如果使用 Microsoft Office 2003,则在已安装适用于 Word、Excel 和 PowerPoint 2007 文件格式的 Microsoft Office 兼容包的情况下,可以将文档保存为 Office Open XML 格式。 有关更多信息或要查看 ECMA Office Open XML 规范,请参见 OpenXML 网站。 首先要了解的是 WordprocessingML 文档的形状。 WordprocessingML 文档包含一个正文元素(称为 w:body),该元素包含文档的各个段落。 每个段落包含一个或多个文本域(称为 w:r)。 每个文本域包含一 个或多个文本块(称为 w:t)。 下面是一个非常简单的 WordprocessingML 文档: 此文档包含两个段落。 这两个段落都包含单个文本域,每个文本域都包含单个文本块。 查看 XML 格式的 WordprocessingML 文档内容的最简单方式是使用 Microsoft Word 创建一个 XML 文档,保存 该文档,然后运行下面的程序,将该 XML 打印到控制台。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 Xml 复制代码 This is a paragraph. This is another paragraph. C# 复制代码 const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; using (Package wdPackage = Package.Open("SampleDoc.docx", FileMode.Open, FileAccess.Read)) { PackageRelationship relationship = wdPackage .GetRelationshipsByType(documentRelationshipType) .FirstOrDefault(); if (relationship != null) { 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5d5b54f5-fd27-4... 外部资源 请参见 Uri documentUri = PackUriHelper.ResolvePartUri( new Uri("/", UriKind.Relative), relationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Get the officeDocument part from the package. // Load the XML in the part into an XDocument instance. XDocument xdoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); Console.WriteLine(xdoc.Root); } } Ecma 介绍 Office (2007) Open XML 文件格式 概述 Office 2003: XML Reference Schemas Download page(Office 2003:XML 参考架构下载页) 概念 教程: 操作 WordprocessingML 文档中的内容 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5d5b54f5-fd27-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 创建源 Office Open XML 文档 请参见 发送反馈意见 本主题演示如何创建本教程其他示例中使用的 Office Open XML WordprocessingML 文档。 如果您按照这些说明操 作,您的输出结果将与每个示例中提供的输出相匹配。 不过,本教程中的示例可以使用任何有效的 WordprocessingML 文档。 若要创建本教程使用的文档,您必须安装 2007 Microsoft Office 系统,或者具有适用于 Word、Excel 和 PowerPoint 2007 文件格式的 Microsoft Office 兼容性包的 Microsoft Office 2003。 创建 WordprocessingML 文档 请参见 创建 WordprocessingML 文档 1. 创建一个新的 Microsoft Word 文档。 2. 将以下文本粘贴到新文档中: 3. 用“标题 1”样式格式化第一行。 4. 选择包含 C# 代码的行。 第一行以 using 关键字开头。 最后一行是最后一个右大括号。 用 courier 字体格式化这些行。 用新样式格式化这些行并将新样式命名为“Code”。 5. 最后,选择包含输出的整个行,并用 Code 样式格式化该行。 6. 保存文档,并将其命名为 SampleDoc.docx。 如果使用的是 Microsoft Word 2003,请在“保存类型” 下拉列表中选择“Word 2007 文档”。 复制代码 Parsing WordprocessingML with LINQ to XML The following example prints to the console. using System; class Program { public static void Main(string[] args) { Console.WriteLine("Hello World"); } } This example produces the following output: Hello World 概念 教程: 操作 WordprocessingML 文档中的内容 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3ea8ccbf-0854-47... 全部折叠 代码:C# 语言集成查询 (LINQ) 查找默认段落样式 请参见 发送反馈意见 在 WordprocessingML 文档中操作信息教程中的第一项任务是在文档中查找默认段落样式。 示例 说明 下面的示例打开一个 Office Open XML WordprocessingML 文档,查找文档和包的样式部分,然后执行查找默认样式 名称的查询。 有关 Office Open XML 文档包和构成包的各个部分的信息,请参见 Office Open XML WordprocessingML 文档的详细信息。 查询将查找名为 w:style 的节点,该节点具有值为“paragraph”的名为 w:type 的属性和值为“1”的名为 w:default 的属性。 由于将只有一个 XML 节点具有这些属性,因此查询使用 Enumerable.First 运算符将集合转换 为单一实例。 然后,它获取名为 w:styleId 的属性的值。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 代码 注释 本示例生成以下输出: C# 复制代码 const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } // The following query finds all the paragraphs that have the default style. string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph"&& (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); Console.WriteLine("The default style is: {0}", defaultStyle); 复制代码 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/2271ab2c-7be1-4... 后续步骤 请参见 The default style is: Normal 在下一个示例中,我们将创建一个类似的查询,查找文档中的所有段落及其样式: z 检索段落及其样式 概念 教程: 操作 WordprocessingML 文档中的内容 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/2271ab2c-7be1-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 检索段落及其样式 请参见 发送反馈意见 在本示例中,我们编写一个从 WordprocessingML 文档检索段落节点的查询。 它还标识每个段落的样式。 此查询建立在前一示例查找默认段落样式中的查询的基础之上,该查询从样式列表中检索默认样式。 这些信息是必需的,以便查询能标识未显式设置样式的段 落样式。 段落样式通过 w:pPr 元素来设置;如果段落未包含此元素,则它会使用默认样式的格式。 本主题解释某些查询的意义,然后作为一个完整工作示例的一部分来显示查询。 示例 示例 示例 检索文档中所有段落及其样式的查询的源如下所示: 此表达式与前一示例查找默认段落样式中的查询的源相似。 主要区别是它使用 Descendants 轴,而不是 Elements 轴。 该查询使用 Descendants 轴,因 为在包含节的文档中,段落将不是正文元素的直接子元素;而是在层次结构中的两个级别之下。 通过使用 Descendants 轴,无论文档是否使用节,代码 都能运行。 C# 复制代码 xDoc.Root.Element(w + "body").Descendants(w + "p") 该查询使用 let 子句来确定包含样式节点的元素。 如果没有任何元素,则将 styleNode 设置为 null: let 子句首先使用 Elements 轴,查找所有名为 pPr 的元素,然后使用 Elements 扩展方法,查找所有名为 pStyle 的子元素,最后使用 FirstOrDefault 标 准查询运算符,将集合转换为单一实例。 如果集合为空,则将 styleNode 设置为 null。 这是一个用于查找 pStyle 子代节点的有用方法。 请注意,如 果 pPr 子节点不存在,代码不会通过引发异常而运行失败;相反,它会将 styleNode 设置为 null,这是此 let 子句的期望行为。 该查询投影一个具有两个成员 StyleName 和 ParagraphNode 的匿名类型的集合。 C# 复制代码 let styleNode = para.Elements(w + "pPr").Elements(w + "pStyle").FirstOrDefault() 本示例处理一个 WordprocessingML 文档,它从 WordprocessingML 文档中检索段落节点。 它还标识每个段落的样式。 本示例以本教程中前面的一些示例 为基础构建。 下面代码中的注释标识出了这个新查询。 在创建源 Office Open XML 文档中,对本示例源文档的创建进行了说明。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph"&& (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); // Following is the new query that finds all paragraphs in the // document and their styles. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/baf1efc6-3c92-47... 后续步骤 请参见 当此示例应用于 创建源 Office Open XML 文档 中说明的文档时,会生成以下输出。 ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; foreach (var p in paragraphs) Console.WriteLine("StyleName:{0}", p.StyleName); 复制代码 StyleName:Heading1 StyleName:Normal StyleName:Normal StyleName:Normal StyleName:Code StyleName:Code StyleName:Code StyleName:Code StyleName:Code StyleName:Code StyleName:Code StyleName:Normal StyleName:Normal StyleName:Normal StyleName:Code 在下一个主题检索段落的文本中,我们将创建一个查询,以检索段落的文本。 概念 教程: 操作 WordprocessingML 文档中的内容 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/baf1efc6-3c92-47... 全部折叠 代码:C# 语言集成查询 (LINQ) 检索段落的文本 请参见 发送反馈意见 此示例以上一个示例检索段落及其样式为基础。 这个新示例将每个段落的文本作为字符串进行检索。 为检索文本,此示例另外添加了一个查询,该查询循环访问匿名类型的集合,并通过添加新成员 Text 对一个匿名类型的 新集合进行投影。 该示例使用 Aggregate 标准查询运算符将多个字符串串联为一个字符串。 这种方法(即首先投影到一个匿名类型集合,然后使用该集合投影到一个新的匿名类型集合)是一种既常用又有效的方 法。 此查询在编写时本来可以不投影到第一个匿名类型。 但是因为使用迟缓计算,即使这样做也并不会过多占用额外的 处理能力。 此方法在堆上创建更多生存时间很短的对象,但这并不会显著降低性能。 当然,可以只编写一个查询,使之包含检索段落、每个段落的样式以及每个段落的文本这些功能。 但是,将一个比较复杂 的查询分解成多个查询通常很有好处,因为这样产生的代码更加模块化,更易于维护。 而且,如果需要重用查询的某一部 分,使用此方式编写的查询更容易重构。 这些链接在一起的查询使用的处理模型在教程: 将查询链接在一起主题中有详细的讨论。 示例 本示例处理一个 WordprocessingML 文档,它确定元素节点、样式名称和每个段落的文本。 本示例以本教程中前面 的一些示例为基础构建。 下面代码中的注释标识出了这个新查询。 有关创建本示例的源文档的说明,请参见创建源 Office Open XML 文档。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph"&& (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); // Find all paragraphs in the document. var paragraphs = from para in xDoc 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e3832674-22f8-4... 后续步骤 请参见 当此示例应用于 创建源 Office Open XML 文档 中说明的文档时,会生成以下输出。 .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Following is the new query that retrieves the text of // each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = para .ParagraphNode .Elements(w + "r") .Elements(w + "t") .Aggregate( new StringBuilder(), (s, i) => s.Append((string)i), s => s.ToString() ) }; foreach (var p in paraWithText) Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text); 复制代码 StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML< StyleName:Normal >< StyleName:Normal >The following example prints to the console.< StyleName:Normal >< StyleName:Code >using System;< StyleName:Code >< StyleName:Code >class Program {< StyleName:Code > public static void (string[] args) {< StyleName:Code > Console.WriteLine("Hello World");< StyleName:Code > }< StyleName:Code >}< StyleName:Normal >< StyleName:Normal >This example produces the following output:< StyleName:Normal >< StyleName:Code >Hello World< 下面的示例演示如何使用扩展方法而不是 Aggregate 将多个字符串串联为一个字符串。 z 使用扩展方法进行重构 概念 教程: 操作 WordprocessingML 文档中的内容 LINQ to XML 中的延迟执行和迟缓计算 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e3832674-22f8-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用扩展方法进行重构 请参见 发送反馈意见 本示例建立在前面示例(检索段落的文本)的基础之上,具体方法是使用作为扩展方法实现的一个纯函数来重构字符串的串 联。 前面的示例使用 Aggregate 标准查询运算符将多个字符串串联为一个字符串。 不过,编写一个扩展方法来执行此操作会更 方便,因为这样实现的查询会更小、更简单。 示例 示例 本示例处理一个 WordprocessingML 文档,检索段落、每个段落的样式以及每个段落的文本。 本示例以本教程中前面 的一些示例为基础构建。 本示例包含 StringConcatenate 方法的多个重载。 在创建源 Office Open XML 文档中,对本示例源文档的创建进行了说明。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } StringConcatenate 方法有四个重载。 其中一个重载仅接受字符串集合并返回单个字符串。 另一个重载可以接受任 何类型的集合,以及从该集合的单一实例映射到字符串的委托。 使用其余两个重载可以指定分隔符字符串。 下面的代码使用所有四个重载。 本示例生成以下输出: C# 复制代码 string[] numbers = { "one", "two", "three" }; Console.WriteLine("{0}", numbers.StringConcatenate()); Console.WriteLine("{0}", numbers.StringConcatenate(":")); int[] intNumbers = { 1, 2, 3 }; Console.WriteLine("{0}", intNumbers.StringConcatenate(i => i.ToString())); Console.WriteLine("{0}", intNumbers.StringConcatenate(i => i.ToString(), ":")); 复制代码 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/84e2a80a-7b69-4... 示例 onetwothree one:two:three: 123 1:2:3: 现在,可以对示例进行修改,从而利用新扩展方法。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/84e2a80a-7b69-4... 后续步骤 请参见 当此示例应用于 创建源 Office Open XML 文档 中说明的文档时,会生成以下输出。 请注意,此重构是重构到纯函数的一种变体。 下一主题将更详细地介绍重构到纯函数的概念。 PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph" && (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); // Find all paragraphs in the document. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Retrieve the text of each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = para .ParagraphNode .Elements(w + "r") .Elements(w + "t") .StringConcatenate(e => (string)e) }; foreach (var p in paraWithText) Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text); } } 复制代码 StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML< StyleName:Normal >< StyleName:Normal >The following example prints to the console.< StyleName:Normal >< StyleName:Code >using System;< StyleName:Code >< StyleName:Code >class Program {< StyleName:Code > public static void (string[] args) {< StyleName:Code > Console.WriteLine("Hello World");< StyleName:Code > }< StyleName:Code >}< StyleName:Normal >< StyleName:Normal >This example produces the following output:< StyleName:Normal >< StyleName:Code >Hello World< 下一示例演示如何使用纯函数以其他方式重构此代码: z 使用纯函数进行重构 概念 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/84e2a80a-7b69-4... 教程: 操作 WordprocessingML 文档中的内容 重构为纯函数 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/84e2a80a-7b69-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用纯函数进行重构 请参见 发送反馈意见 下面的示例使用一个纯函数对前面的示例使用扩展方法进行重构进行重构。在本示例中,查找段落文本的代码将移至纯静态方法 ParagraphText 中。 示例 本示例处理一个 WordprocessingML 文档,它从 WordprocessingML 文档中检索段落节点。 它还标识每个段落的样式。 本示例 以本教程中前面的一些示例为基础构建。 下面代码中的注释标识出了重构的代码。 有关创建本示例的源文档的说明,请参见创建源 Office Open XML 文档。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { // This is a new method that assembles the paragraph text. public static string ParagraphText(XElement e) { XNamespace w = e.Name.Namespace; return e .Elements(w + "r") .Elements(w + "t") .StringConcatenate(element => (string)element); } static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e5f10d21-9c9b-4... [vb] PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph" && (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); // Find all paragraphs in the document. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Retrieve the text of each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = ParagraphText(para.ParagraphNode) }; foreach (var p in paraWithText) Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text); } } 复制代码 Imports Module Module1 _ Public Function StringConcatenate(ByVal source As IEnumerable(Of String)) As String Dim sb As StringBuilder = New StringBuilder() For Each s As String In source sb.Append(s) Next Return sb.ToString() End Function _ Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _ ByVal func As Func(Of T, String)) As String Dim sb As StringBuilder = New StringBuilder() For Each item As T In source sb.Append(func(item)) Next Return sb.ToString() End Function _ Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _ ByVal separator As String) As String Dim sb As StringBuilder = New StringBuilder() For Each s As T In source sb.Append(s).Append(separator) Next Return sb.ToString() 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e5f10d21-9c9b-4... End Function _ Public Function StringConcatenate(Of T)(ByVal source As IEnumerable(Of T), _ ByVal func As Func(Of T, String), ByVal separator As String) As String Dim sb As StringBuilder = New StringBuilder() For Each item As T In source sb.Append(func(item)).Append(separator) Next Return sb.ToString() End Function ' This is a new method that assembles the paragraph text. Public Function ParagraphText(ByVal e As XElement) As String Dim w As XNamespace = e.Name.Namespace Return (e..).StringConcatenate(Function(element) CStr(element)) End Function ' Following function is required because VB does not support short circuit evaluation Private Function GetStyleOfParagraph(ByVal styleNode As XElement, _ ByVal defaultStyle As String) As String If (styleNode Is Nothing) Then Return defaultStyle Else Return styleNode.@w:val End If End Function Sub Main() Dim fileName = "SampleDoc.docx" Dim documentRelationshipType = _ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Dim stylesRelationshipType = _ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Dim wordmlNamespace = _ "http://schemas.openxmlformats.org/wordprocessingml/2006/main" Dim xDoc As XDocument = Nothing Dim styleDoc As XDocument = Nothing Using wdPackage As Package = Package.Open(fileName, FileMode.Open, FileAccess.Read) Dim docPackageRelationship As PackageRelationship = _ wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault() If (docPackageRelationship IsNot Nothing) Then Dim documentUri As Uri = PackUriHelper.ResolvePartUri(New Uri("/", UriKind.Relative), _ docPackageRelationship.TargetUri) Dim documentPart As PackagePart = wdPackage.GetPart(documentUri) ' Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())) ' Find the styles part. There will only be one. Dim styleRelation As PackageRelationship = _ documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault() If (Not (styleRelation Is Nothing)) Then Dim styleUri As Uri = _ PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri) Dim stylePart As PackagePart = wdPackage.GetPart(styleUri) ' Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())) End If End If End Using Dim defaultStyle As String = _ ( _ From style In styleDoc.Root. _ Where style.@w:type = "paragraph" And _ style.@w:default = "1" _ Select style _ ).First().@w:styleId ' Find all paragraphs in the document. Dim paragraphs = _ From para In xDoc.Root.... _ Let styleNode As XElement = para...FirstOrDefault _ Select New With { _ .ParagraphNode = para, _ .StyleName = GetStyleOfParagraph(styleNode, defaultStyle) _ } ' Retrieve the text of each paragraph. Dim paraWithText = _ From para In paragraphs _ Select New With { _ .ParagraphNode = para.ParagraphNode, _ .StyleName = para.StyleName, _ .Text = ParagraphText(para.ParagraphNode) _ } For Each p In paraWithText Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text) Next 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e5f10d21-9c9b-4... 请参见 此示例生成与重构前相同的输出: 后续步骤 下面的示例演示如何将 XML 投影到一个不同的形状: z 对不同形状的 XML 进行投影 End Sub End Module 复制代码 StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML< StyleName:Normal >< StyleName:Normal >The following example prints to the console.< StyleName:Normal >< StyleName:Code >using System;< StyleName:Code >< StyleName:Code >class Program {< StyleName:Code > public static void (string[] args) {< StyleName:Code > Console.WriteLine("Hello World");< StyleName:Code > }< StyleName:Code >}< StyleName:Normal >< StyleName:Normal >This example produces the following output:< StyleName:Normal >< StyleName:Code >Hello World< 概念 教程: 操作 WordprocessingML 文档中的内容 使用扩展方法进行重构 重构为纯函数 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e5f10d21-9c9b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 对不同形状的 XML 进行投影 请参见 发送反馈意见 本主题演示对形状不同于源 XML 的 XML 进行投影的示例。 许多典型的 XML 转换由链接的查询组成,如本示例中所示。 一种很常见的做法是从某种格式的 XML 开始,将中间结果投 影为匿名类型或命名类型的集合,最后将结果投影回与源 XML 形状完全不同的 XML。 示例 本示例处理一个 WordprocessingML 文档,它从 WordprocessingML 文档中检索段落节点。 本示例还标识每个段落的 样式和文本。 最后,本示例将以不同的形状投影 XML。 本示例以本教程中前面的一些示例为基础构建。 下面代码 中的注释标识出了执行投影操作的新语句。 有关创建本示例的源文档的说明,请参见创建源 Office Open XML 文档。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { public static string ParagraphText(XElement e) { XNamespace w = e.Name.Namespace; return e .Elements(w + "r") .Elements(w + "t") .StringConcatenate(element => (string)element); } static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e675ed1e-9971-4... 本示例生成以下输出: XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph" && (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); // Find all paragraphs in the document. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Retrieve the text of each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = ParagraphText(para.ParagraphNode) }; // The following is the new code that projects XML in a new shape. XElement root = new XElement("Root", from p in paraWithText select new XElement("Paragraph", new XElement("StyleName", p.StyleName), new XElement("Text", p.Text) ) ); Console.WriteLine(root); } } Xml 复制代码 Heading1 Parsing WordprocessingML with LINQ to XML 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e675ed1e-9971-4... 后续步骤 请参见 Normal Normal The following example prints to the console. Normal Code using System; Code Code class Program { Code public static void (string[] args) { Code Console.WriteLine("Hello World"); Code } Code } Normal Normal This example produces the following output: Normal Code Hello World 下面的示例将通过查询查找 Word 文档中的所有文本: z 在 Word 文档中查找文本 概念 教程: 操作 WordprocessingML 文档中的内容 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e675ed1e-9971-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 在 Word 文档中查找文本 请参见 发送反馈意见 本主题扩展了以前的查询,以执行一些有用的任务:在文档中查找一个字符串的所有匹配项。 示例 本示例处理一个 WordprocessingML 文档,在该文档中查找特定文本片断的所有匹配项。 为演示此操作,我们使用一个查询,查找字符串“Hello”。 本示例以本教 程中前面的一些示例为基础构建。 下面代码中的注释标识出了这个新查询。 有关创建本示例的源文档的说明,请参见创建源 Office Open XML 文档。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { public static string ParagraphText(XElement e) { XNamespace w = e.Name.Namespace; return e .Elements(w + "r") .Elements(w + "t") .StringConcatenate(element => (string)element); } static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph" && (string)style.Attribute(w + "default") == "1" 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c7096d56-9b8c-4... 本示例生成以下输出: 当然,您可以修改搜索条件,使它搜索具有特定样式的行。 下面的查询查找具有“Code”样式的所有空行: select style ).First().Attribute(w + "styleId"); // Find all paragraphs in the document. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Retrieve the text of each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = ParagraphText(para.ParagraphNode) }; // Following is the new query that retrieves all paragraphs // that have specific text in them. var helloParagraphs = from para in paraWithText where para.Text.Contains("Hello") select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = para.Text }; foreach (var p in helloParagraphs) Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text); } } 复制代码 StyleName:Code > Console.WriteLine("Hello World");< StyleName:Code >Hello World< C# 复制代码 public static class LocalExtensions { public static string StringConcatenate(this IEnumerable source) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, string separator) { StringBuilder sb = new StringBuilder(); foreach (string s in source) sb.Append(s).Append(separator); return sb.ToString(); } public static string StringConcatenate(this IEnumerable source, Func func, string separator) { StringBuilder sb = new StringBuilder(); foreach (T item in source) sb.Append(func(item)).Append(separator); return sb.ToString(); } } class Program { public static string ParagraphText(XElement e) { XNamespace w = e.Name.Namespace; return e .Elements(w + "r") .Elements(w + "t") .StringConcatenate(element => (string)element); } static void Main(string[] args) { const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; XDocument xDoc = null; 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c7096d56-9b8c-4... 后续步骤 请参见 本示例生成以下输出: 当然,可以通过多种方式对此示例进行改进。 例如,可以使用正则表达式来搜索文本,可以循环访问某一特定目录下的所有 Word 文件等等。 请注意,此示例的执行效果与将它编写为一个单个查询几乎同样好。 因为每个查询都以迟缓方式实现,每个查询直到循环到该查询时才生成结果。 有关执行和迟缓 计算的更多信息,请参见LINQ to XML 中的延迟执行和迟缓计算。 XDocument styleDoc = null; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); } } } string defaultStyle = (string)( from style in styleDoc.Root.Elements(w + "style") where (string)style.Attribute(w + "type") == "paragraph" && (string)style.Attribute(w + "default") == "1" select style ).First().Attribute(w + "styleId"); // Find all paragraphs in the document. var paragraphs = from para in xDoc .Root .Element(w + "body") .Descendants(w + "p") let styleNode = para .Elements(w + "pPr") .Elements(w + "pStyle") .FirstOrDefault() select new { ParagraphNode = para, StyleName = styleNode != null ? (string)styleNode.Attribute(w + "val") : defaultStyle }; // Retrieve the text of each paragraph. var paraWithText = from para in paragraphs select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = ParagraphText(para.ParagraphNode) }; // Retrieve all paragraphs that have no text and are styled Code. var blankCodeParagraphs = from para in paraWithText where para.Text == "" && para.StyleName == "Code" select new { ParagraphNode = para.ParagraphNode, StyleName = para.StyleName, Text = para.Text }; foreach (var p in blankCodeParagraphs) Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text); } } 复制代码 StyleName:Code >< 下一节提供有关 WordprocessingML 文档的更多信息: z Office Open XML WordprocessingML 文档的详细信息 概念 教程: 操作 WordprocessingML 文档中的内容 使用纯函数进行重构 LINQ to XML 中的延迟执行和迟缓计算 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c7096d56-9b8c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) Office Open XML WordprocessingML 文档的详细信息 请参见 发送反馈意见 本节提供有关 Office Open XML WordprocessingML 文档详情的信息。 它演示 Open XML 文档的文档和样式部分的 示例。 本节内容 请参见 主题 说明 使用样式的 WordprocessingML 文档 演示 Office Open XML WordprocessingML 文档的文档部分。 WordprocessingML 文档的样式 部分 演示 Office Open XML WordprocessingML 文档的样式部分。 输出 Office Open XML 文档部 分的示例 提供一个打开 Office Open XML WordprocessingML 文档,并将文档和 样式部分输出到控制台的示例。 概念 教程: 操作 WordprocessingML 文档中的内容 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/adcf7f96-3e7c-4d... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用样式的 WordprocessingML 文档 请参见 发送反馈意见 较为复杂的 WordprocessingML 文档具有使用样式格式的段落。 对 WordprocessingML 文档的结构做一些注释是很有帮助的。 WordprocessingML 文档存储在包中。 包具有多个部分(“部分”在包上下文中具有明确 的意义,部分实质上是压缩在一起组成包的多个文件)。 如果一个文档包含使用样式格式的段落,将会有一个文档部分包含应用了样式的段落。 还会 有一个样式部分包含文档引用的样式。 访问包时,重要的是通过部分之间的关系来访问包,而不是使用任意路径来访问包。 此问题超出了在 WordprocessingML 文档中使用内容教程的范围, 但是本教程包含的示例程序演示了正确的方法。 使用样式的文档 WordprocessingML 文档的形状主题中提供的 WordML 示例是非常简单的文档。 下面的文档则更为复杂:它具有使用样式格式的段落。 查看构成 Office Open XML 文档的 XML 的最简单方法是运行输出 Office Open XML 文档部分的示例。 在下面的文档中,第一段具有 Heading1 样式。 很多段落具有默认样式。 还有一些段落具有 Code 样式。 由于这种相对复杂性,这个文档更值 得使用 LINQ to XML 来解析。 在那些使用非默认样式的段落中,段落元素具有名为 w:pPr 的子元素,该子元素又具有子元素 w:pStyle。 该元素具有属性 w:val,该属性包 含样式名称。 如果段落具有默认样式,则意味着段落元素不具有 w:p.Pr 子元素。 Xml 复制代码 Parsing WordprocessingML with LINQ to XML The following example prints to the console. using System; class Program { public static void Main (string[] args) { Console.WriteLine("Hello World"); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c090ffdf-223b-4ff... 请参见 } } This example produces the following output: Hello World 概念 Office Open XML WordprocessingML 文档的详细信息 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c090ffdf-223b-4ff... 全部折叠 代码:C# 语言集成查询 (LINQ) WordprocessingML 文档的样式部分 请参见 发送反馈意见 本主题演示 Office Open XML WordprocessingML 文档的样式部分的示例。 示例 下面的示例是构成 Office Open XML WordprocessingML 文档的样式部分的 XML。 默认段落样式有一个具有下面开始标记的元素: 在编写用于查找默认样式标识符的查询时需要知道此信息,以便查询能够识别具有默认样式的段落的样式。 请注意,与 Microsoft Word 生成的典型文档相比,这些文档非常简单。 在许多情况下,Word 会保存大量附 加信息、附加格式设置和元数据。 而且,Word 不会将行设置为像本示例这样易于阅读的格式;而在保存 XML 时不会带缩进。 不过,所有 WordprocessingML 文档都具有相同的基本 XML 形状。 因此,本教程中演 示的查询将适用于更复杂的文档。 复制代码 Xml 复制代码 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/08bc3228-86e2-4... 请参见 概念 Office Open XML WordprocessingML 文档的详细信息 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/08bc3228-86e2-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 输出 Office Open XML 文档部分的示例 请参见 发送反馈意见 本主题演示如何打开 Office Open XML 文档并访问其中的部分。 示例 请参见 下面的示例打开 Office Open XML 文档,并将文档部分和样式部分输出到控制台。 本示例使用 WindowsBase 程序集中的类。 它使用 System.IO.Packaging 命名空间中的类型。 C# 复制代码 const string fileName = "SampleDoc.docx"; const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; const string stylesRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; XNamespace w = wordmlNamespace; using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read)) { PackageRelationship docPackageRelationship = wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault(); if (docPackageRelationship != null) { Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), docPackageRelationship.TargetUri); PackagePart documentPart = wdPackage.GetPart(documentUri); // Load the document XML in the part into an XDocument instance. XDocument xdoc = XDocument.Load(XmlReader.Create(documentPart.GetStream())); Console.WriteLine("TargetUri:{0}", docPackageRelationship.TargetUri); Console.WriteLine("=================================================================="); Console.WriteLine(xdoc.Root); Console.WriteLine(); // Find the styles part. There will only be one. PackageRelationship styleRelation = documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault(); if (styleRelation != null) { Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri); PackagePart stylePart = wdPackage.GetPart(styleUri); // Load the style XML in the part into an XDocument instance. XDocument styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream())); Console.WriteLine("TargetUri:{0}", styleRelation.TargetUri); Console.WriteLine("=================================================================="); Console.WriteLine(styleDoc.Root); Console.WriteLine(); } } } 概念 Office Open XML WordprocessingML 文档的详细信息 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/9f906a07-a726-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 修改 XML 树 (LINQ to XML) 请参见 发送反馈意见 LINQ to XML 是一个 XML 树在内存中的存储区。 在从源中加载或解析 XML 树之后,LINQ to XML 允许您就地修改 该树,然后序列化该树,可以将它保存到文件中或发送到远程服务器。 就地修改树时,可使用某些方法,例如 Add。 但是有另外一种方法,就是使用函数构造来生成具有不同形状的新树。 根据需要对 XML 树所做的更改类型的不 同,以及根据树大小的不同,该方法可能更加强大,更易于开发。 本节第一个主题将比较这两种方法。 本节内容 请参见 主题 说明 内存中 XML 树修改与函数构造 (LINQ to XML) 在内存中修改 XML 树与使用函数构造的比较。 向 XML 树中添加元素、属性和节点 提供有关向 XML 树中添加元素、属性或节点的信息。 修改 XML 树中的元素、属性和节点 提供有关修改现有元素、属性或节点的信息。 从 XML 树中移除元素、属性和节点 提供有关从 XML 树中移除元素、属性或节点的信息。 维护名称/值对 描述如何维护最好保存为名称/值对的应用程序信息,例如配置 信息或全局设置。 如何: 更改整个 XML 树的命名空间 演示如何将 XML 树从一个命名空间移动到另一个命名空间。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/cfc0790a-ce59-4b... 全部折叠 代码:C# 语言集成查询 (LINQ) 内存中 XML 树修改与函数构造 (LINQ to XML) 请参见 发送反馈意见 就地修改 XML 树是更改 XML 文档形状的传统方法。 典型的应用程序将文档加载到数据存储区(如 DOM 或 LINQ to XML);使用编程接口插入节点、删除节点或更改节点的内容;然后将 XML 保存到文件或通过网络传输。 LINQ to XML 允许使用另一种可在许多方案中使用的方法:函数构造。 函数构造将修改数据视为转换问题,而不是 数据存储区的具体操作。 如果您采用某种数据表示形式并有效地将其从一种形式转换为另一种形式,其结果等效于 您采用一个数据存储区并对其以某种方式进行操作以采用另一种形状。 函数构造方法的关键是将查询的结果传递给 XDocument 和 XElement 构造函数。 在许多情况下,您可以在操作数据存储区所需的很短时间内编写转换代码,并且该代码更稳定、更易于维护。 在这 些情况下,虽然转换方法可能具有较强的处理能力,但它更适合用于修改数据。 如果开发人员熟悉函数方法,则在 很多情况下,生成的代码会更易于理解。 可以很容易地找到修改树的每部分的代码。 DOM 程序员更熟悉就地修改 XML 树的方法,而不了解这种方法的开发人员可能会对使用函数方法编写的代码感到 陌生。 如果您必须对大型 XML 树进行小修改,则在很多情况下,用于就地修改树的方法将花费更少的 CPU 时间。 本主题提供用这两种方法实现的一个示例。 将属性转换为元素 此示例假设您想修改下面的简单 XML 文档,使属性变为元素。 本节首先介绍传统的就地修改方法。 然后显 示函数构造方法。 修改 XML 树 您可以编写一些过程代码以便从属性创建元素,然后删除属性,如下所示: 此代码生成以下输出: 函数构造方法 相比之下,函数方法包含用于形成新树的代码、从源树中选择元素和属性并在将其添加到新树中时进行相应的 转换。 函数方法如下所示: 本示例与第一个示例输出相同的 XML。 但请注意,您可以在函数方法中实际看到生成的新 XML 的结构。 您 可以看到创建 Root 元素的代码、从源树中提取 Child1 元素的代码和将源树中的属性转换为新树中的元素的 代码。 Xml 复制代码 Content C# 复制代码 XElement root = XElement.Load("Data.xml"); foreach (XAttribute att in root.Attributes()) { root.Add(new XElement(att.Name, (string)att)); } root.Attributes().Remove(); Console.WriteLine(root); Xml 复制代码 Content 123 456 C# 复制代码 XElement root = XElement.Load("Data.xml"); XElement newTree = new XElement("Root", root.Element("Child1"), from att in root.Attributes() select new XElement(att.Name, (string)att) ); Console.WriteLine(newTree); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/24f92a38-17a5-4f... 请参见 在本例中,函数示例一点也不比第一个示例简短,而且一点也不比第一个示例简单。 但如果要对一个 XML 树 进行很多更改,则非函数方法将变得非常复杂,而且会显得很笨拙。 相比之下,使用函数方法时,您只需形 成所需的 XML,嵌入适当的查询和表达式以提取需要的内容。 函数方法生成的代码更易于维护。 请注意,在本例中,函数方法的执行效果可能没有树操作方法好。 主要问题是函数方法创建了更多短生存期 的对象。 但是,如果使用函数方法能够提高程序员的效率,则折中也是一种有效的方式。 这是一个很简单的示例,但它显示了这两种方法之间基本原理上的差异。 对于转换较大的 XML 文档,函数方 法可以产生更高的效率增益。 概念 修改 XML 树 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/24f92a38-17a5-4f... 全部折叠 代码:C# 语言集成查询 (LINQ) 向 XML 树中添加元素、属性和节点 请参见 发送反馈意见 可以向现有的 XML 树中添加内容(包括元素、属性、注释、处理指令、文本和 CDATA)。 添加内容的方法 示例 下面的方法将子内容添加到 XElement 或 XDocument 中: 下面的方法将内容添加为 XNode 的同级节点。 向其中添加同级内容的最常见的节点是 XElement,不过你也 可以将有效的同级内容添加到其他类型的节点,例如 XText 或 XComment。 方法 说明 Add 在 XContainer 的子内容的末尾添加内容。 AddFirst 在 XContainer 的子内容的开头添加内容。 方法 说明 AddAfterSelf 在 XNode 后面添加内容。 AddBeforeSelf 在 XNode 前面添加内容。 说明 下面的示例创建两个 XML 树,然后修改其中一个树。 代码 注释 此代码产生以下输出: C# 复制代码 XElement srcTree = new XElement("Root", new XElement("Element1", 1), new XElement("Element2", 2), new XElement("Element3", 3), new XElement("Element4", 4), new XElement("Element5", 5) ); XElement xmlTree = new XElement("Root", new XElement("Child1", 1), new XElement("Child2", 2), new XElement("Child3", 3), new XElement("Child4", 4), new XElement("Child5", 5) ); xmlTree.Add(new XElement("NewChild", "new content")); xmlTree.Add( from el in srcTree.Elements() where (int)el > 3 select el ); // Even though Child9 does not exist in srcTree, the following statement will not // throw an exception, and nothing will be added to xmlTree. xmlTree.Add(srcTree.Element("Child9")); Console.WriteLine(xmlTree); Xml 复制代码 1 2 3 4 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/cc728c31-a06e-4... 请参见 5 new content 4 5 概念 修改 XML 树 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/cc728c31-a06e-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 修改 XML 树中的元素、属性和节点 请参见 发送反馈意见 下表汇总了修改元素、元素的子元素或元素属性 (Attribute) 时可以使用的方法和属性 (Property)。 下面的方法修改 XElement。 下面的方法修改 XAttribute。 下面的方法修改 XNode(包括 XElement 或 XDocument)。 下面的方法修改 XContainer(XElement 或 XDocument)。 请参见 方法 说明 XElement.Parse 用已分析的 XML 替换元素。 XElement.RemoveAll 移除元素的所有内容(子节点和属性)。 XElement.RemoveAttributes 移除元素的属性。 XElement.ReplaceAll 替换元素的所有内容(子节点和属性)。 XElement.ReplaceAttributes 替换元素的属性。 XElement.SetAttributeValue 设置属性的值。 如果该属性不存在,则创建该属性。 如果值设置为 null,则 移除该属性。 XElement.SetElementValue 设置子元素的值。 如果该元素不存在,则创建该元素。 如果值设置为 null, 则移除该元素。 XElement.Value 用指定的文本替换元素的内容(子节点)。 XElement.SetValue 设置元素的值。 方法 说明 XAttribute.Value 设置属性的值。 XAttribute.SetValue 设置属性的值。 方法 说明 XNode.ReplaceWith 用新内容替换节点。 方法 说明 XContainer.ReplaceNodes 用新内容替换子节点。 概念 修改 XML 树 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7f5359c4-ea5f-4e... 全部折叠 代码:C# 语言集成查询 (LINQ) 从 XML 树中移除元素、属性和节点 请参见 发送反馈意见 可以修改 XML 树,移除元素、属性和其他类型的节点。 从 XML 文档中移除单个元素或单个属性的操作非常简单。 但是,若要移除多个元素或属性的集合,则应首先将一 个集合具体化为一个列表,然后从该列表中删除相应元素或属性。 最好的方法是使用 Remove 扩展方法,该方法可 以实现此操作。 这么做的主要原因在于,从 XML 树检索的大多数集合都是用延迟执行生成的。 如果不首先将集合具体化为列表, 或者不使用扩展方法,则可能会遇到某类 Bug。 有关更多信息,请参见混合声明性代码/命令性代码的问题 (C#) (LINQ to XML)。 下列方法可以从 XML 树中移除节点和属性。 示例 方法 说明 [M:System.Xml.Linq.XAttribute.Remove()] 从父属性中移除 XAttribute。 [M:System.Xml.Linq.XContainer.RemoveNodes()] 从 XContainer 中移除子节点。 XElement.RemoveAll 从 XElement 中移除内容和属性。 XElement.RemoveAttributes 移除 XElement 的属性。 XElement.SetAttributeValue 如果传递 null 作为值,则移除该属性。 XElement.SetElementValue 如果传递 null 作为值,则移除该子元素。 XNode.Remove 从父节点中移除 XNode。 Extensions.Remove 从父元素中移除源集合中的每个属性或元素。 说明 此示例演示三种移除元素的方法。 第一种,移除单个元素。 第二种,检索元素的集合,使用 Enumerable.ToList(TSource) 运算符将它们具体化,然后移除集合。 最后一种,检索元素的集合,使用 Remove 扩展方法移除元素。 有关 ToList(TSource) 运算符的更多信息,请参见Converting Data Types。 代码 注释 C# 复制代码 XElement root = XElement.Parse(@" "); root.Element("Child1").Element("GrandChild1").Remove(); root.Element("Child2").Elements().ToList().Remove(); root.Element("Child3").Elements().Remove(); Console.WriteLine(root); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7c9b5ad2-dffc-47... 请参见 此代码生成以下输出: 请注意,第一个孙元素已从 Child1 中移除。 所有孙元素都已从 Child2 和 Child3 中移除。 Xml 复制代码 概念 修改 XML 树 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7c9b5ad2-dffc-47... 全部折叠 代码:C# 语言集成查询 (LINQ) 维护名称/值对 请参见 发送反馈意见 很多应用程序都必须维护需要保存为名称/值对的信息。 这种信息可以是配置信息或全局设置。LINQ to XML 包含一 些属性,可以轻松保存一组名称/值对。 可以将这些信息保存为属性,也可以保存为一组子元素。 将信息保存为属性和保存为子元素之间的区别在于属性具有约束,对于一个元素,只能有一个属性具有特定的名 称。 而这种限制不适用于子元素。 SetAttributeValue 与 SetElementValue 示例 示例 两种有助于保存名称/值对的方法是 SetAttributeValue 和 SetElementValue。 这两种方法具有相似的语义。 SetAttributeValue 可以添加、修改或移除元素的属性。 z 如果使用不存在的属性的名称调用 SetAttributeValue,则该方法会创建一个新属性并将该属性添加到指定 的元素中。 z 如果使用现有属性的名称调用 SetAttributeValue,并指定一些内容,则会用指定内容替换属性的内容。 z 如果使用现有属性的名称调用 SetAttributeValue,并指定内容为空,则会从该属性的父级移除该属性。 SetElementValue 可以添加、修改或移除元素的子元素。 z 如果使用不存在的子元素的名称调用 SetElementValue,则该方法会创建一个新元素并将该新元素添加到 指定的元素中。 z 如果使用现有元素的名称调用 SetElementValue,并指定一些内容,则会用指定内容替换元素的内容。 z 如果使用现有元素的名称调用 SetElementValue,并指定内容为空,则会从该元素的父级移除该元素。 下面的示例创建一个没有属性的元素。 之后使用 SetAttributeValue 方法创建一个名称/值对列表并维护该列 表。 此示例产生以下输出: C# 复制代码 // Create an element with no content. XElement root = new XElement("Root"); // Add a number of name/value pairs as attributes. root.SetAttributeValue("Top", 22); root.SetAttributeValue("Left", 20); root.SetAttributeValue("Bottom", 122); root.SetAttributeValue("Right", 300); root.SetAttributeValue("DefaultColor", "Color.Red"); Console.WriteLine(root); // Replace the value of Top. root.SetAttributeValue("Top", 10); Console.WriteLine(root); // Remove DefaultColor. root.SetAttributeValue("DefaultColor", null); Console.WriteLine(root); 复制代码 下面的示例创建一个没有子元素的元素。 之后使用 SetElementValue 方法创建一个名称/值对列表并维护该列 表。 C# 复制代码 // Create an element with no content. XElement root = new XElement("Root"); // Add a number of name/value pairs as elements. 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fe9798bf-b5e2-4a... 请参见 此示例产生以下输出: root.SetElementValue("Top", 22); root.SetElementValue("Left", 20); root.SetElementValue("Bottom", 122); root.SetElementValue("Right", 300); root.SetElementValue("DefaultColor", "Color.Red"); Console.WriteLine(root); Console.WriteLine("----"); // Replace the value of Top. root.SetElementValue("Top", 10); Console.WriteLine(root); Console.WriteLine("----"); // Remove DefaultColor. root.SetElementValue("DefaultColor", null); Console.WriteLine(root); 复制代码 22 20 122 300 Color.Red ---- 10 20 122 300 Color.Red ---- 10 20 122 300 概念 修改 XML 树 (LINQ to XML) 参考 SetAttributeValue SetElementValue 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/fe9798bf-b5e2-4a... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 更改整个 XML 树的命名空间 示例 请参见 发送反馈意见 有时需要以编程方式更改元素或属性的命名空间。 LINQ to XML 可简化此任务。 可以设置 XElement.Name 属性。 不能设 置 XAttribute.Name 属性 (Property),但是很容易将属性 (Attribute) 复制到 System.Collections.Generic.List(T) 中,移除现 有属性 (Attribute),然后添加位于新命名空间中的新属性 (Attribute)。 有关更多信息,请参见使用 XML 命名空间。 示例 请参见 下面的代码创建两个不在命名空间中的 XML 树。 然后更改每个树的命名空间,再将它们合并到一个树中。 本示例生成以下输出: C# 复制代码 XElement tree1 = new XElement("Data", new XElement("Child", "content", new XAttribute("MyAttr", "content") ) ); XElement tree2 = new XElement("Data", new XElement("Child", "content", new XAttribute("MyAttr", "content") ) ); XNamespace aw = "http://www.adventure-works.com"; XNamespace ad = "http://www.adatum.com"; // change the namespace of every element and attribute in the first tree foreach (XElement el in tree1.DescendantsAndSelf()) { el.Name = aw.GetName(el.Name.LocalName); List atList = el.Attributes().ToList(); el.Attributes().Remove(); foreach (XAttribute at in atList) el.Add(new XAttribute(aw.GetName(at.Name.LocalName), at.Value)); } // change the namespace of every element and attribute in the second tree foreach (XElement el in tree2.DescendantsAndSelf()) { el.Name = ad.GetName(el.Name.LocalName); List atList = el.Attributes().ToList(); el.Attributes().Remove(); foreach (XAttribute at in atList) el.Add(new XAttribute(ad.GetName(at.Name.LocalName), at.Value)); } // add attribute namespaces so that the tree will be serialized with // the aw and ad namespace prefixes tree1.Add( new XAttribute(XNamespace.Xmlns + "aw", "http://www.adventure-works.com") ); tree2.Add( new XAttribute(XNamespace.Xmlns + "ad", "http://www.adatum.com") ); // create a new composite tree XElement root = new XElement("Root", tree1, tree2 ); Console.WriteLine(root); Xml 复制代码 content content 概念 修改 XML 树 (LINQ to XML) 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f75731a4-9b00-4... 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f75731a4-9b00-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 高级 LINQ to XML 编程 请参见 发送反馈意见 本节为高级开发人员提供一些只适用于某些 LINQ to XML 方案的信息。 本节内容 请参见 主题 说明 LINQ to XML 批注 介绍如何将批注添加到 LINQ to XML 节点和属性。 LINQ to XML 事件 介绍如何为更改 XML 树时所发生的事件编写事件处理程序。 使用节点编程 介绍如何在比元素和属性更精细的粒度级别上查询和操作节点。 混合声明性代码/命令性代码的 问题 (C#) (LINQ to XML) 介绍在混合声明性代码(查询)与命令性代码(修改 XML 树的代码) 时所出现的问题。 如何: 流处理可访问标头信息 的 XML 片段 介绍如何在 XmlReader 中对 XML 片段进行流式处理。 使用此技术, 可以控制应用程序的内存需求量。 如何: 执行大型 XML 文档的流 式转换 介绍如何在 XmlReader 中对 XML 进行流式处理,如何转换 XML 片 段,如何使用 XStreamingElement 对输出进行流式处理。 如何: 读取和写入编码的文档 介绍如何读取和写入经过编码的 XML 文档。 使用 XSLT 转换 XML 树 介绍如何使用 XSLT 转换 XML 树。 如何: 使用批注转换 XSLT 样 式中的 LINQ to XML 树 介绍如何使用批注来帮助进行 XML 树的转换。 序列化包含 XElement 或 XDocument 对象的对象图 介绍如何序列化包含 XElement 和 XDocument 对象的对象图。 使用 LINQ to XML 的 WPF 数据 绑定 介绍如何将 LINQ to XML 用作 Windows Presentation Foundation 应用 程序中数据绑定的数据源。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/88c4a6ea-700b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 批注 请参见 发送反馈意见 LINQ to XML 中的批注可将任意类型的任意对象与 XML 树中的任何 XML 组件相关联。 若要向 XML 组件(例如 XElement 或 XAttribute)中添加批注,需调用 AddAnnotation 方法。 批注是按类型检索 的。 请注意,批注不是 XML 信息集的一部分,不对它们进行序列化和反序列化。 方法 请参见 处理批注时可以使用以下方法: 方法 说明 AddAnnotation 向 XObject 的批注列表中添加对象。 Annotation 从 XObject 获取指定类型的第一个批注对象。 Annotations 获取 XObject 的指定类型的批注集合。 RemoveAnnotations 从 XObject 移除指定类型的批注。 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/e2f0052d-61e2-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 事件 请参见 发送反馈意见 LINQ to XML 事件让您可以在 XML 树发生改变时得到通知。 可以将事件添加到任何 XObject 的实例。 事件处理程序然后将接收对该 XObject 及其所有子代进行修改的事件。 例 如,可以将事件处理程序添加到树根,然后从该事件处理程序中处理对树进行的所有修改。 有关 LINQ to XML 事件的示例,请参见 Changing 和 Changed。 类型和事件 示例 在处理事件时使用下面的类型: 修改 XML 树时将引发以下事件: 类型 说明 XObjectChange 当 XObject 发生事件时指定事件类型。 XObjectChangeEventArgs 为 Changing 和 Changed 事件提供数据。 事件 说明 Changing 在此 XObject 或它的任何子代即将发生更改之前发生。 Changed 在 XObject 或它的任何子代已经更改时发生。 说明 当您希望在 XML 树中保留一些聚合信息时事件非常有用。 例如,您可能想保留一份发票合计,计算发票上各个 项目的总和。 本示例使用事件来维护复杂元素 Items 之下所有子元素的总和。 代码 C# 复制代码 XElement root = new XElement("Root", new XElement("Total", "0"), new XElement("Items") ); XElement total = root.Element("Total"); XElement items = root.Element("Items"); items.Changed += (object sender, XObjectChangeEventArgs cea) => { switch (cea.ObjectChange) { case XObjectChange.Add: if (sender is XElement) total.Value = ((int)total + (int)(XElement)sender).ToString(); if (sender is XText) total.Value = ((int)total + (int)((XText)sender).Parent).ToString(); break; case XObjectChange.Remove: if (sender is XElement) total.Value = ((int)total - (int)(XElement)sender).ToString(); if (sender is XText) total.Value = ((int)total - Int32.Parse(((XText)sender).Value)).ToString(); break; } Console.WriteLine("Changed {0} {1}", sender.GetType().ToString(), cea.ObjectChange.ToString()); }; items.SetElementValue("Item1", 25); items.SetElementValue("Item2", 50); items.SetElementValue("Item2", 75); items.SetElementValue("Item3", 133); items.SetElementValue("Item1", null); items.SetElementValue("Item4", 100); Console.WriteLine("Total:{0}", (int)total); Console.WriteLine(root); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/eed5177c-4e9c-4... 请参见 注释 此代码生成以下输出: 复制代码 Changed System.Xml.Linq.XElement Add Changed System.Xml.Linq.XElement Add Changed System.Xml.Linq.XText Remove Changed System.Xml.Linq.XText Add Changed System.Xml.Linq.XElement Add Changed System.Xml.Linq.XElement Remove Changed System.Xml.Linq.XElement Add Total:308 308 75 133 100 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/eed5177c-4e9c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用节点编程 请参见 发送反馈意见 需要编写 XML 编辑器、转换系统或报告编写器这类程序的 LINQ to XML 开发人员通常需要编写在比元素和属性更细的 粒度下运行的程序。 开发人员通常需要在节点级别上工作,操作文本节点、处理指令和添加注释。 本主题提供有关在 节点级别进行编程的一些详细信息。 节点详细信息 在节点级别上工作的程序员需要了解有关编程的许多细节。 XDocument 的子节点的父属性设置为 Null Parent 属性包含父 XElement,而不是父节点。 XDocument 的子节点没有父 XElement。 它们的父级是文档,因 此这些节点的 Parent 属性设置为 null。 下面的示例演示这一操作: 本示例生成以下输出: 可以存在相邻文本节点 在许多 XML 编程模型中,相邻的文本节点始终会合并到一起。 这有时也称为文本节点的规范化。LINQ to XML 不 规范化文本节点。 如果向同一个元素添加两个文本节点,则会产生相邻文本节点。 但如果将指定内容添加为字 符串而不是 XText 节点,则 LINQ to XML 可能会将该字符串与相邻的文本节点合并在一起。 下面的示例演示这一操作: 本示例生成以下输出: 可以存在空文本节点 在某些 XML 编程模型中,文本节点保证不包含空字符串。 原因是这种文本节点对 XML 的序列化没有影响。 但 由于可以存在相邻文本节点这一相同的原因,如果您通过将文本节点的值设置为空字符串来移除文本节点中的文 本,则不会删除文本节点本身。 C# 复制代码 XDocument doc = XDocument.Parse(@""); Console.WriteLine(doc.Nodes().OfType().First().Parent == null); Console.WriteLine(doc.Root.Parent == null); 复制代码 True True C# 复制代码 XElement xmlTree = new XElement("Root", "Content"); Console.WriteLine(xmlTree.Nodes().OfType().Count()); // this does not add a new text node xmlTree.Add("new content"); Console.WriteLine(xmlTree.Nodes().OfType().Count()); // this does add a new, adjacent text node xmlTree.Add(new XText("more text")); Console.WriteLine(xmlTree.Nodes().OfType().Count()); 复制代码 1 1 2 C# 复制代码 XElement xmlTree = new XElement("Root", "Content"); XText textNode = xmlTree.Nodes().OfType().First(); // the following line does not cause the removal of the text node. textNode.Value = ""; XText textNode2 = xmlTree.Nodes().OfType().First(); 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/18680d1c-6e7c-4... 本示例生成以下输出: 空文本节点影响序列化 如果一个元素仅包含一个空的文本节点,则会用长标记语法序列化该元素: . 如果一个元素不 包含任何子节点,则会用短标记语法序列化该元素: . 本示例生成以下输出: 命名空间是 LINQ to XML 树中的属性 即使命名空间声明与属性具有完全相同的语法,在某些编程接口(如 XSLT 和 XPath)中,也不会将命名空间声 明视为属性。 但在 LINQ to XML 中,命名空间存储为 XML 树中的 XAttribute 对象。 如果您循环访问包含命名 空间声明的元素的属性,则会在返回的集合中看到作为一项列出的命名空间声明。 IsNamespaceDeclaration 属性 (property) 指示某一属性 (attribute) 是否为命名空间声明。 本示例生成以下输出: XPath 轴方法不返回 XDocument 的子空白 LINQ to XML 允许 XDocument 具有子文本节点,但这些文本节点只能包含空白。 但是,XPath 对象模型不包括空 白作为文档的子节点,因此在使用 Nodes 轴循环访问 XDocument 的子级时,将返回空白文本节点。 但在使用 XPath 轴方法循环访问 XDocument 的子级时,不会返回空白文本节点。 本示例生成以下输出: Console.WriteLine(">>{0}<<", textNode2); 复制代码 >><< C# 复制代码 XElement child1 = new XElement("Child1", new XText("") ); XElement child2 = new XElement("Child2"); Console.WriteLine(child1); Console.WriteLine(child2); 复制代码 C# 复制代码 XElement root = XElement.Parse( @""); foreach (XAttribute att in root.Attributes()) Console.WriteLine("{0} IsNamespaceDeclaration:{1}", att, att.IsNamespaceDeclaration); 复制代码 xmlns="http://www.adventure-works.com" IsNamespaceDeclaration:True xmlns:fc="www.fourthcoffee.com" IsNamespaceDeclaration:True AnAttribute="abc" IsNamespaceDeclaration:False C# 复制代码 // Create a document with some white space child nodes of the document. XDocument root = XDocument.Parse( @" ", LoadOptions.PreserveWhitespace); // count the white space child nodes using LINQ to XML Console.WriteLine(root.Nodes().OfType().Count()); // count the white space child nodes using XPathEvaluate Console.WriteLine(((IEnumerable)root.XPathEvaluate("text()")).OfType().Count()); 复制代码 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/18680d1c-6e7c-4... 请参见 XDeclaration 对象不是节点 在循环访问 XDocument 的子节点时,将看不到 XML 声明对象。 它是文档的属性,不是文档的子节点。 本示例生成以下输出: 3 0 C# 复制代码 XDocument doc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XElement("Root") ); doc.Save("Temp.xml"); Console.WriteLine(File.ReadAllText("Temp.xml")); // this shows that there is only one child node of the document Console.WriteLine(doc.Nodes().Count()); 复制代码 1 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/18680d1c-6e7c-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 混合声明性代码/命令性代码的问题 (C#) (LINQ to XML) 请参见 发送反馈意见 LINQ to XML 包含各种不同的方法,使您能够直接修改 XML 树。 您可以添加元素、删除元素、更改元素的内容、添 加属性等等。 在修改 XML 树中详细介绍了此编程接口。 如果循环访问一个轴,例如 Elements,而在循环访问该 轴时正在修改 XML 树,那么可能会发生一些异常问题。 此问题有时称为“万圣节问题”。 问题的定义 循环访问时添加 如果您使用 LINQ 编写循环访问集合的代码,那么您是以声明性风格在编写代码。 这种风格更像是描述您想 要实现的内容,而不是描述您所需要的实现方式。 如果您编写的代码要执行以下操作:1) 获取第一个元素; 2) 测试该元素是否符合某种条件;3) 修改该元素;4) 将该元素放回列表中。那么,此代码就是命令性代码。 您是在告诉计算机您希望以何种方式完成任务。 在同一操作中混合这些代码风格正是导致问题的原因。 考虑以下情况: 假设您有一个链接列表,其中有三个项(a、b 和 c): a -> b -> c 现在,假设您想在链接列表中移动,并添加三个新项(a'、b' 和 c')。 您希望生成如下所示的链接列表: a -> a' -> b -> b' -> c -> c' 因此您编写代码来循环访问该列表,在每一项后面紧接着添加一个新项。 而实际发生的情况是,您的代码首 先将看到 a 元素,然后在它后面插入 a'。 现在,您的代码将移动到列表中的下一节点,此时下一节点是 a'! 它会不假思索地向列表中添加一个新项 a''。 在实际中该如何解决这一问题? 您可以创建原始链接列表的副本,然后创建一个全新的列表。 或者,如果您 正在编写纯粹的命令性代码,您可以找到第一项,添加新项,然后在链接列表中前进两步,跳过刚添加的元 素。 例如,假设您希望编写一段代码,让它为树中的每个元素创建一个重复的元素: 此代码将进入一个无限循环。 foreach 语句循环访问 Elements() 轴,将新元素添加到 doc 元素。 结果它 也循环访问刚添加的元素。 由于它在每次循环访问中都分配新对象,最终将会耗尽所有可用内存。 可以通过使用 ToList(TSource) 标准查询运算符将集合放入内存来修复此问题,如下所示: 现在代码可以正常工作了。 生成的 XML 树如下所示: C# 复制代码 XElement root = new XElement("Root", new XElement("A", "1"), new XElement("B", "2"), new XElement("C", "3") ); foreach (XElement e in root.Elements()) root.Add(new XElement(e.Name, (string)e)); C# 复制代码 XElement root = new XElement("Root", new XElement("A", "1"), new XElement("B", "2"), new XElement("C", "3") ); foreach (XElement e in root.Elements().ToList()) root.Add(new XElement(e.Name, (string)e)); Console.WriteLine(root); Xml 复制代码 1 2 3 1 2 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d9a5c404-2b0e-4... 循环访问时删除 为何 LINQ 不能自动处理此问题? 3 如果想要删除某一级别上的所有节点,您可能立即想到编写类似如下所示的代码: 然而,此代码执行起来不会如您所愿。 在这种情况下,在移除第一个元素 A 之后,将从包含在根中的 XML 树中移除该元素,Elements 方法中执行循环访问的代码将找不到下一个元素。 前面的代码产生以下输出: 解决方法仍是调用 ToList(TSource) 来具体化集合,如下所示: 此代码产生以下输出: 或者,可以通过对父元素调用 RemoveAll 来完全消除循环: C# 复制代码 XElement root = new XElement("Root", new XElement("A", "1"), new XElement("B", "2"), new XElement("C", "3") ); foreach (XElement e in root.Elements()) e.Remove(); Console.WriteLine(root); Xml 复制代码 2 3 C# 复制代码 XElement root = new XElement("Root", new XElement("A", "1"), new XElement("B", "2"), new XElement("C", "3") ); foreach (XElement e in root.Elements().ToList()) e.Remove(); Console.WriteLine(root); Xml 复制代码 C# 复制代码 XElement root = new XElement("Root", new XElement("A", "1"), new XElement("B", "2"), new XElement("C", "3") ); root.RemoveAll(); Console.WriteLine(root); 一种方法是总是将所有内容放入内存,而不是执行迟缓计算。 但是,在性能和内存使用方面,这种方法开销 非常大。 事实上,如果 LINQ 和 (LINQ to XML) 采用此方法,在实际情况中将会失败。 另一种可能的方法是将某种事务语法放入 LINQ,让编译器尝试分析代码并确定是否需要具体化任何特定的集 合。 但是,尝试确定具有副作用的所有代码将会非常复杂。 考虑以下代码: 这种分析代码需要分析 TestSomeCondition 和 DoMyProjection 方法,以及这些方法调用的所有方法,以此来 确定是否有任何代码会产生副作用。 但是,分析代码不能简单地查找所有具有副作用的代码。 在此情况下, C# 复制代码 var z = from e in root.Elements() where TestSomeCondition(e) select DoMyProjection(e); 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d9a5c404-2b0e-4... 指导 请参见 它需要选择只对 root 的子元素具有副作用的代码。 LINQ to XML 不会尝试进行任何这样的分析。 因此,避免这些问题就靠您了。 首先,不要将声明性代码和命令性代码混合。 即便您确切知道您的集合的语义以及修改 XML 树的方法的语义,可以更巧妙地编写代码来避免这类问题,但 您的代码将来需要由其他开发人员来维护,而他们可能并不像您那样清楚这些问题。 如果混合使用声明性和 命令性编码风格,您的代码将更加脆弱。 如果您编写代码将集合具体化从而避免这些问题,应在代码中适当加以注释,使负责维护的程序员能够了解问 题所在。 其次,在性能和其他因素允许的情况下,仅使用声明性代码。 不要修改现有的 XML 树, 而是生成一个新 树。 C# 复制代码 XElement root = new XElement("Root", new XElement("A", "1"), new XElement("B", "2"), new XElement("C", "3") ); XElement newRoot = new XElement("Root", root.Elements(), root.Elements() ); Console.WriteLine(newRoot); 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/d9a5c404-2b0e-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 流处理可访问标头信息的 XML 片段 示例 请参见 发送反馈意见 有时,您必须读取任意大的 XML 文件并在编写您的应用程序时可以预测应用程序的内存需求量。 如果您试图用大 XML 文件填充 XML 树,则内存占用量将与文件大小成正比,也就是说会占用过多内存。 因此,您应改用流处理技 术。 一种选择是使用 XmlReader 来编写应用程序。 但您可能需要使用 LINQ 来查询 XML 树。 在这种情况下,您可以 编写自己的自定义轴方法。 有关更多信息,请参见如何: 编写 LINQ to XML 轴方法。 若要编写您自己的轴方法,请编写一个小方法,让该方法使用 XmlReader 来读取各个节点,直到达到您感兴趣的节 点之一。 该方法然后调用 ReadFrom,后者将从 XmlReader 中读取数据并实例化 XML 片段。 然后,该方法生成 从 yield return 到该方法(枚举您的自定义轴方法的方法)的每个片段。 然后,您可以对自定义轴方法编写 LINQ 查询。 流处理技术最适合只需处理一次源文档的情况,您可以按文档顺序处理各个元素。 某些标准查询运算符(如 OrderBy)可以循环访问其源、收集所有数据、对数据排序,最后生成序列中的第一项。 请注意,如果使用可在生 成第一项之前具体化源的查询运算符,则不会保持小的内存需求量。 示例 有时,问题会变得更有意思。 在下面的 XML 文档中,自定义轴方法的使用方也必须知道每一项所属的使用方 名称。 本示例采用的方法还将监视此标头信息、保存标头信息,然后生成包含标头信息和所要枚举的详细信息的小型 XML 树。 该轴方法然后生成这个新的小型 XML 树。 之后,查询将可以访问标头信息以及详细信息。 此方法具有小的内存需求量。 由于生成了所有的详细 XML 片段,不再需要保留对前一个片段的引用,因此, 此方法可用于垃圾回收。 请注意,此技术会在堆上创建许多短生存期的对象。 Xml 复制代码 A. Datum Corporation 0001 0002 0003 0004 Fabrikam, Inc. 0005 0006 0007 0008 Southridge Video 0009 0010 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/01311522-9d03-4... 下面的示例演示如何实现和使用可流处理由 URI 指定的文件中的 XML 片段的自定义轴方法。 此自定义轴经 过专门编写,可以处理具有 Customer、Name 和 Item 元素,并且这些元素按上述 Source.xml 文档排列的 文档。 这是一个过于简单的实现。 将会准备一个更可靠的实现以分析无效文档。 此代码生成以下输出: 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 static IEnumerable StreamCustomerItem(string uri) { using (XmlReader reader = XmlReader.Create(uri)) { XElement name = null; XElement item = null; reader.MoveToContent(); // Parse the file, save header information when encountered, and yield the // Item XElement objects as they are created. // loop through Customer elements while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Customer") { // move to Name element while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name") { name = XElement.ReadFrom(reader) as XElement; break; } } // loop through Item elements while (reader.Read()) { if (reader.NodeType == XmlNodeType.EndElement) break; if (reader.NodeType == XmlNodeType.Element && reader.Name == "Item") { item = XElement.ReadFrom(reader) as XElement; if (item != null) { XElement tempRoot = new XElement("Root", new XElement(name) ); tempRoot.Add(item); yield return item; } } } } } } } static void Main(string[] args) { XElement xmlTree = new XElement("Root", from el in StreamCustomerItem("Source.xml") where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7 select new XElement("Item", new XElement("Customer", (string)el.Parent.Element("Name")), new XElement(el.Element("Key")) ) ); Console.WriteLine(xmlTree); } Xml 复制代码 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/01311522-9d03-4... 请参见 A. Datum Corporation 0003 A. Datum Corporation 0004 Fabrikam, Inc. 0005 Fabrikam, Inc. 0006 Fabrikam, Inc. 0007 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/01311522-9d03-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 执行大型 XML 文档的流式转换 示例 请参见 发送反馈意见 有时,您必须转换任意大的 XML 文件并在编写您的应用程序时可以预测应用程序的内存需求量。 如果您试图用大 XML 文件填充 XML 树,则内存占用量将与文件大小成正比,也就是说会占用过多内存。 因此,您应改用流处理技 术。 流处理技术最适合只需处理一次源文档的情况,您可以按文档顺序处理各个元素。 某些标准查询运算符(如 OrderBy)可以循环访问其源、收集所有数据、对数据排序,最后生成序列中的第一项。 请注意,如果使用可在生 成第一项之前具体化源的查询运算符,则不会使应用程序保持小的内存需求量。 即使使用如何:流处理可访问标头信息的 XML 片段中说明的技术,在试图装配包含转换的文档的 XML 树时,内存 占用量也会过大。 主要方法有两种。 一种方法是使用 XStreamingElement 的延迟处理特性。 另一种方法是创建一个 XmlWriter 并使 用 LINQ to XML 的功能将元素写入 XmlWriter。 本主题演示这两种方法。 示例 下面的示例在如何: 流处理可访问标头信息的 XML 片段中的示例的基础上生成。 本示例使用 XStreamingElement 的延迟执行功能对输出进行流式处理。 本示例可在保持很小的内存需求量的 同时转换非常大的文档。 请注意,自定义轴 (StreamCustomerItem) 经过专门编写,可以处理具有 Customer、Name 和 Item 元素, 并且这些元素将按下面 Source.xml 文档排列的文档。 不过,将会准备一个更可靠的实现以分析无效文档。 下面是源文档 Source.xml: 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 Xml 复制代码 A. Datum Corporation 0001 0002 0003 0004 Fabrikam, Inc. 0005 0006 0007 0008 Southridge Video 0009 0010 页码,1/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0759e428-6e59-4... 此代码生成以下输出: C# 复制代码 static IEnumerable StreamCustomerItem(string uri) { using (XmlReader reader = XmlReader.Create(uri)) { XElement name = null; XElement item = null; reader.MoveToContent(); // Parse the file, save header information when encountered, and yield the // Item XElement objects as they are created. // loop through Customer elements while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Customer") { // move to Name element while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name") { name = XElement.ReadFrom(reader) as XElement; break; } } // loop through Item elements while (reader.Read()) { if (reader.NodeType == XmlNodeType.EndElement) break; if (reader.NodeType == XmlNodeType.Element && reader.Name == "Item") { item = XElement.ReadFrom(reader) as XElement; if (item != null) { XElement tempRoot = new XElement("Root", new XElement(name) ); tempRoot.Add(item); yield return item; } } } } } } } static void Main(string[] args) { XStreamingElement root = new XStreamingElement("Root", from el in StreamCustomerItem("Source.xml") select new XElement("Item", new XElement("Customer", (string)el.Parent.Element("Name")), new XElement(el.Element("Key")) ) ); root.Save("Test.xml"); Console.WriteLine(File.ReadAllText("Test.xml")); } Xml 复制代码 A. Datum Corporation 0001 页码,2/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0759e428-6e59-4... 下面的示例也在如何: 流处理可访问标头信息的 XML 片段中的示例的基础上生成。 本示例使用 LINQ to XML 的功能将元素写入 XmlWriter。 本示例可在保持很小的内存需求量的同时转换非常 大的文档。 请注意,自定义轴 (StreamCustomerItem) 经过专门编写,可以处理具有 Customer、Name 和 Item 元素, 并且这些元素将按下面 Source.xml 文档排列的文档。 不过,更可靠的实现将会使用 XSD 验证源文档或将会 准备一个更可靠的实现以分析无效文档。 本示例与本主题中的前一示例使用同一个源文档 Source.xml。 它也生成完全相同的输出。 使用 XStreamingElement 对输出 XML 进行流式处理胜于写入到 XmlWriter。 A. Datum Corporation 0002 A. Datum Corporation 0003 A. Datum Corporation 0004 Fabrikam, Inc. 0005 Fabrikam, Inc. 0006 Fabrikam, Inc. 0007 Fabrikam, Inc. 0008 Southridge Video 0009 Southridge Video 0010 注意: 下面的示例使用 C# 的 yield return 构造。 由于 Visual Basic 2008 中没有等效的功能,因此只提供 C# 示例。 C# 复制代码 static IEnumerable StreamCustomerItem(string uri) { using (XmlReader reader = XmlReader.Create(uri)) { XElement name = null; XElement item = null; reader.MoveToContent(); // Parse the file, save header information when encountered, and yield the // Item XElement objects as they are created. // loop through Customer elements while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Customer") { // move to Name element while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name") { name = XElement.ReadFrom(reader) as XElement; 页码,3/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0759e428-6e59-4... 此代码生成以下输出: break; } } // loop through Item elements while (reader.Read()) { if (reader.NodeType == XmlNodeType.EndElement) break; if (reader.NodeType == XmlNodeType.Element && reader.Name == "Item") { item = XElement.ReadFrom(reader) as XElement; if (item != null) { XElement tempRoot = new XElement("Root", new XElement(name) ); tempRoot.Add(item); yield return item; } } } } } } } static void Main(string[] args) { IEnumerable srcTree = from el in StreamCustomerItem("Source.xml") select new XElement("Item", new XElement("Customer", (string)el.Parent.Element("Name")), new XElement(el.Element("Key")) ); XmlWriterSettings xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; xws.Indent = true; using (XmlWriter xw = XmlWriter.Create("Output.xml", xws)) { xw.WriteStartElement("Root"); foreach (XElement el in srcTree) el.WriteTo(xw); xw.WriteEndElement(); } string str = File.ReadAllText("Output.xml"); Console.WriteLine(str); } Xml 复制代码 A. Datum Corporation 0001 A. Datum Corporation 0002 A. Datum Corporation 0003 A. Datum Corporation 0004 Fabrikam, Inc. 0005 Fabrikam, Inc. 0006 Fabrikam, Inc. 0007 页码,4/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0759e428-6e59-4... 请参见 Fabrikam, Inc. 0008 Southridge Video 0009 Southridge Video 0010 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,5/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0759e428-6e59-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 读取和写入编码的文档 示例 请参见 发送反馈意见 若要创建编码的 XML 文档,请向 XML 树中添加一个 XDeclaration,将编码设置为需要的代码页名称。 由 WebName 返回的任何值都是有效值。 如果您读取编码的文档,则要将 Encoding 属性设置为该代码页名称。 如果将 Encoding 设置为一个有效的代码页名称,则 LINQ to XML 将用指定的编码进行序列化。 示例 请参见 下面的示例创建两个文档,一个文档使用 utf-8 编码,另一个使用 utf-16 编码。 然后加载文档并将编码打印到控制台。 本示例生成以下输出: C# 复制代码 Console.WriteLine("Creating a document with utf-8 encoding"); XDocument encodedDoc8 = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XElement("Root", "Content") ); encodedDoc8.Save("EncodedUtf8.xml"); Console.WriteLine("Encoding is:{0}", encodedDoc8.Declaration.Encoding); Console.WriteLine(); Console.WriteLine("Creating a document with utf-16 encoding"); XDocument encodedDoc16 = new XDocument( new XDeclaration("1.0", "utf-16", "yes"), new XElement("Root", "Content") ); encodedDoc16.Save("EncodedUtf16.xml"); Console.WriteLine("Encoding is:{0}", encodedDoc16.Declaration.Encoding); Console.WriteLine(); XDocument newDoc8 = XDocument.Load("EncodedUtf8.xml"); Console.WriteLine("Encoded document:"); Console.WriteLine(File.ReadAllText("EncodedUtf8.xml")); Console.WriteLine(); Console.WriteLine("Encoding of loaded document is:{0}", newDoc8.Declaration.Encoding); Console.WriteLine(); XDocument newDoc16 = XDocument.Load("EncodedUtf16.xml"); Console.WriteLine("Encoded document:"); Console.WriteLine(File.ReadAllText("EncodedUtf16.xml")); Console.WriteLine(); Console.WriteLine("Encoding of loaded document is:{0}", newDoc16.Declaration.Encoding); 复制代码 Creating a document with utf-8 encoding Encoding is:utf-8 Creating a document with utf-16 encoding Encoding is:utf-16 Encoded document: Content Encoding of loaded document is:utf-8 Encoded document: Content Encoding of loaded document is:utf-16 概念 高级 LINQ to XML 编程 参考 XDeclaration.Encoding 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/215bcf5b-284b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用 XSLT 转换 XML 树 请参见 发送反馈意见 可以创建 XML 树,从 XML 树创建 XmlReader,创建新文档,然后创建 XmlWriter,以写入新文档。 接着,可以调 用 XSLT 转换,将 XmlReader 和 XmlWriter 传入该转换。 在转换成功完成后,使用转换的结果,填充新的 XML 树。 示例 请参见 本示例生成以下输出: C# 复制代码 string xslMarkup = @" "; XDocument xmlTree = new XDocument( new XElement("Parent", new XElement("Child1", "Child1 data"), new XElement("Child2", "Child2 data") ) ); XDocument newTree = new XDocument(); using (XmlWriter writer = newTree.CreateWriter()) { // Load the style sheet. XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load(XmlReader.Create(new StringReader(xslMarkup))); // Execute the transform and output the results to a writer. xslt.Transform(xmlTree.CreateReader(), writer); } Console.WriteLine(newTree); Xml 复制代码 Child1 data Child2 data 概念 高级 LINQ to XML 编程 参考 XContainer.CreateWriter XNode.CreateReader 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b7e4543f-9fd3-42... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用批注转换 XSLT 样式中的 LINQ to XML 树 请参见 发送反馈意见 使用批注可帮助进行 XML 树的转换。 有些 XML 文档“以文档为中心兼有混合内容”。 对于这样的文档,您不必知道元素的子节点的形状。 例如,包含文本的节点可能具有像 下面这样的外观: 任何给定的文本节点都可以具有任意数量的子 元素。 此方法可扩展到很多其他情况:即页面可以包含各种子元素,如规则段 落、带项目符号的段落和位图。 表中的单元格可以包含文本,下拉列表或位图。 以文档为中心的 XML 的一个主要特性是您不必知道任一 特定元素将具有哪些子元素。 如果在转换树中的元素时不必知道有关要转换元素的子级的太多信息,则这种方法(使用批注)就是一种有效的方法。 这种方法摘要如下: z 首先,用替换元素批注树中的元素。 z 然后,循环访问整个树,创建一个新树,并用其批注替换树中的每个元素。 本示例在名为 XForm 的函数中实现迭代和创建新树。 具体地说,此方法包括: z 执行一个或多个 LINQ to XML 查询,用这些查询返回要从一种形状转换为另一种形状的元素集。 对于查询中的每个元素,添加一个新 XElement 对象作为该元素的批注。 在转换的新树中会用此新元素替换批注的元素。 这是示例中所示的唯一需要编写的代码。 z 作为批注添加的新元素可以包含新的子节点,它可以形成一个具有任意形状的子树。 z 有一条特殊规则:如果新元素的子节点位于不同的命名空间,即专门为此建立的命名空间(在本示例中,此命名空间为 http://www.microsoft.com/LinqToXmlTransform/2007),则不会将该子元素复制到新树。 而如果命名空间是上面提到的特殊命名 空间,并且元素的本地名称为 ApplyTransforms,则会迭代源树中该元素的子节点并将其复制到新树(但批注的子元素本身例外,它们 将根据这些规则进行转换)。 z 这有些类似于 XSL 中的转换规范。 用于选择一组节点的查询类似于用于模板的 XPath 表达式。 用于创建以批注形式保存的新 XElement 的代码类似于 XSL 中的序列构造函数,ApplyTransforms 元素的功能类似于 XSL 中的 xsl:apply-templates 元素。 z 采用此方法的优势之一是在用公式表述查询时,您始终是对未修改的源树编写查询。 您不必担心对树所做的修改如何影响要编写的查 询。 转换一个树 更复杂的转换 Xml 复制代码 A phrase with bold and italic text. 下面的第一个示例将所有 Paragraph 节点重命名为 para。 本示例生成以下输出: C# 复制代码 XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; XName at = xf + "ApplyTransforms"; XElement root = XElement.Parse(@" This is a sentence with bold and italic text. More text. "); // replace Paragraph with para foreach (var el in root.Descendants("Paragraph")) el.AddAnnotation( new XElement("para", // same idea as xsl:apply-templates new XElement(xf + "ApplyTransforms") ) ); // The XForm method, shown later in this topic, accomplishes the transform XElement newRoot = XForm(root); Console.WriteLine(newRoot); Xml 复制代码 This is a sentence with bold and italic text. More text. 下面的示例对树进行查询并计算 Data 元素的平均值和总和,并将它们作为新元素添加到树中。 C# 复制代码 XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; XName at = xf + "ApplyTransforms"; XElement data = new XElement("Root", new XElement("Data", 20), new XElement("Data", 10), new XElement("Data", 3) 页码,1/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6d7f448b-031b-4... 实施转换 本示例生成以下输出: ); // while adding annotations, you can query the source tree all you want, // as the tree is not mutated while annotating. data.AddAnnotation( new XElement("Root", new XElement(xf + "ApplyTransforms"), new XElement("Average", String.Format("{0:F4}", data .Elements("Data") .Select(z => (Decimal)z) .Average() ) ), new XElement("Sum", data .Elements("Data") .Select(z => (int)z) .Sum() ) ) ); Console.WriteLine("Before Transform"); Console.WriteLine("----------------"); Console.WriteLine(data); Console.WriteLine(); Console.WriteLine(); // The XForm method, shown later in this topic, accomplishes the transform XElement newData = XForm(data); Console.WriteLine("After Transform"); Console.WriteLine("----------------"); Console.WriteLine(newData); 复制代码 Before Transform ---------------- 20 10 3 After Transform ---------------- 20 10 3 11.0000 33 小函数 XForm 可以从原始的、已批注的树创建新的、转换后的树。 z 该函数的伪代码非常简单: 下面是此函数的实现: 复制代码 The function takes an XElement as an argument and returns an XElement. If an element has an XElement annotation, then Return a new XElement The name of the new XElement is the annotation element's name. All attributes are copied from the annotation to the new node. All child nodes are copied from the annotation, with the exception that the special node xf:ApplyTransforms is recognized, and the source element's child nodes are iterated. If the source child node is not an XElement, it is copied to the new tree. If the source child is an XElement, then it is transformed by calling this function recursively. If an element is not annotated Return a new XElement The name of the new XElement is the source element's name All attributes are copied from the source element to the destination's element. All child nodes are copied from the source element. If the source child node is not an XElement, it is copied to the new tree. If the source child is an XElement, then it is transformed by calling this function recursively. C# 复制代码 // Build a transformed XML tree per the annotations static XElement XForm(XElement source) 页码,2/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6d7f448b-031b-4... 完整示例 { XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; XName at = xf + "ApplyTransforms"; if (source.Annotation() != null) { XElement anno = source.Annotation(); return new XElement(anno.Name, anno.Attributes(), anno .Nodes() .Select( (XNode n) => { XElement annoEl = n as XElement; if (annoEl != null) { if (annoEl.Name == at) return (object)( source.Nodes() .Select( (XNode n2) => { XElement e2 = n2 as XElement; if (e2 == null) return n2; else return XForm(e2); } ) ); else return n; } else return n; } ) ); } else { return new XElement(source.Name, source.Attributes(), source .Nodes() .Select(n => { XElement el = n as XElement; if (el == null) return n; else return XForm(el); } ) ); } } 下面的代码是包括 XForm 函数的完整示例。 它包括此类型转换的几种典型用法: C# 复制代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; class Program { static XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; static XName at = xf + "ApplyTransforms"; // Build a transformed XML tree per the annotations static XElement XForm(XElement source) { if (source.Annotation() != null) { XElement anno = source.Annotation(); return new XElement(anno.Name, anno.Attributes(), anno .Nodes() .Select( (XNode n) => { XElement annoEl = n as XElement; if (annoEl != null) { if (annoEl.Name == at) return (object)( source.Nodes() 页码,3/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6d7f448b-031b-4... .Select( (XNode n2) => { XElement e2 = n2 as XElement; if (e2 == null) return n2; else return XForm(e2); } ) ); else return n; } else return n; } ) ); } else { return new XElement(source.Name, source.Attributes(), source .Nodes() .Select(n => { XElement el = n as XElement; if (el == null) return n; else return XForm(el); } ) ); } } static void Main(string[] args) { XElement root = new XElement("Root", new XComment("A comment"), new XAttribute("Att1", 123), new XElement("Child", 1), new XElement("Child", 2), new XElement("Other", new XElement("GC", 3), new XElement("GC", 4) ), XElement.Parse( "This is an element that " + "has some mixed content"), new XElement("AnUnchangedElement", 42) ); // each of the following serves the same semantic purpose as // XSLT templates and sequence constructors // replace Child with NewChild foreach (var el in root.Elements("Child")) el.AddAnnotation(new XElement("NewChild", (string)el)); // replace first GC with GrandChild, add an attribute foreach (var el in root.Descendants("GC").Take(1)) el.AddAnnotation( new XElement("GrandChild", new XAttribute("ANewAttribute", 999), (string)el ) ); // replace Other with NewOther, add new child elements around original content foreach (var el in root.Elements("Other")) el.AddAnnotation( new XElement("NewOther", new XElement("MyNewChild", 1), // same idea as xsl:apply-templates new XElement(xf + "ApplyTransforms"), new XElement("ChildThatComesAfter") ) ); // change name of element that has mixed content root.Descendants("SomeMixedContent").First().AddAnnotation( new XElement("MixedContent", new XElement(xf + "ApplyTransforms") ) ); // replace with foreach (var el in root.Descendants("b")) el.AddAnnotation( new XElement("Bold", new XElement(xf + "ApplyTransforms") ) ); 页码,4/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6d7f448b-031b-4... 请参见 本示例生成以下输出: // replace with foreach (var el in root.Descendants("i")) el.AddAnnotation( new XElement("Italic", new XElement(xf + "ApplyTransforms") ) ); Console.WriteLine("Before Transform"); Console.WriteLine("----------------"); Console.WriteLine(root); Console.WriteLine(); Console.WriteLine(); XElement newRoot = XForm(root); Console.WriteLine("After Transform"); Console.WriteLine("----------------"); Console.WriteLine(newRoot); } } 复制代码 Before Transform ---------------- 1 2 3 4 This is an element that has some mixed content 42 After Transform ---------------- 1 2 1 3 4 This is an element that has some mixed content 42 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,5/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/6d7f448b-031b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 序列化包含 XElement 或 XDocument 对象的对象图 请参见 发送反馈意见 本主题介绍序列化对象图功能,其中对象图包含对类型为 XElement 和 XDocument 的对象的引用。 为便于执行这 种类型的序列化,XElement 和 XDocument 都实现 IXmlSerializable 接口。 请注意,只有 XElement 和 XDocument 类才实现序列化。 不支持包含 DTD 的 XDocument 的序列化。 本节内容 请参见 主题 说明 如何: 使用 XmlSerializer 进行序列化 演示如何使用 XmlSerializer 进行序列化。 如何: 使用 DataContractSerializer 进行序列化 演示如何使用 DataContractSerializer 进行序列化。 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/443d7904-a6f0-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用 XmlSerializer 进行序列化 示例 请参见 发送反馈意见 本主题显示一个使用 XmlSerializer 进行序列化和反序列化的示例。 示例 下面的示例创建多个包含 XDocument 和 XElement 对象的对象。 然后将它们序列化为内存流,接着从内存 流对它们进行反序列化。 本示例生成以下输出: C# 复制代码 using System; using System.IO; using System.Linq; using System.Xml; using System.Xml.Serialization; using System.Xml.Linq; public class XElementContainer { public XElement member; public XElementContainer() { member = XLinqTest.CreateXElement(); } public override string ToString() { return member.ToString(); } } public class XElementNullContainer { public XElement member; public XElementNullContainer() { } } class XLinqTest { static void Main(string[] args) { Test(new XElementNullContainer()); Test(CreateXElement()); Test(new XElementContainer()); } public static XElement CreateXElement() { XNamespace ns = "http://www.adventure-works.com"; return new XElement(ns + "aw"); } static void Test(T obj) { using (MemoryStream stream = new MemoryStream()) { XmlSerializer s = new XmlSerializer(typeof(T)); Console.WriteLine("Testing for type: {0}", typeof(T)); s.Serialize(XmlWriter.Create(stream), obj); stream.Flush(); stream.Seek(0, SeekOrigin.Begin); object o = s.Deserialize(XmlReader.Create(stream)); Console.WriteLine(" Deserialized type: {0}", o.GetType()); } } } 复制代码 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/90a66dff-c195-4b... 请参见 Testing for type: XElementNullContainer Deserialized type: XElementNullContainer Testing for type: System.Xml.Linq.XElement Deserialized type: System.Xml.Linq.XElement Testing for type: XElementContainer Deserialized type: XElementContainer 概念 序列化包含 XElement 或 XDocument 对象的对象图 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/90a66dff-c195-4b... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 使用 DataContractSerializer 进行序列化 示例 请参见 发送反馈意见 本主题显示一个使用 DataContractSerializer 进行序列化和反序列化的示例。 示例 下面的示例创建多个包含 XDocument 和 XElement 对象的对象。 然后将它们序列化为文本文件,接着从文本文件 对它们进行反序列化。 本示例生成以下输出: C# 复制代码 using System; using System.Xml; using System.Xml.Linq; using System.IO; using System.Runtime.Serialization; public class XLinqTest { public static void Main() { Test(CreateXElement()); Test(new XElementContainer()); Test(new XElementNullContainer()); } public static void Test(T obj) { DataContractSerializer s = new DataContractSerializer(typeof(T)); using (FileStream fs = File.Open("test" + typeof(T).Name + ".xml", FileMode.Create)) { Console.WriteLine("Testing for type: {0}", typeof(T)); s.WriteObject(fs, obj); } using (FileStream fs = File.Open("test" + typeof(T).Name + ".xml", FileMode.Open)) { object s2 = s.ReadObject(fs); if (s2 == null) Console.WriteLine(" Deserialized object is null (Nothing in VB)"); else Console.WriteLine(" Deserialized type: {0}", s2.GetType()); } } public static XElement CreateXElement() { return new XElement(XName.Get("NameInNamespace", "http://www.adventure-works.org")); } } [DataContract] public class XElementContainer { [DataMember] public XElement member; public XElementContainer() { member = XLinqTest.CreateXElement(); } } [DataContract] public class XElementNullContainer { [DataMember] public XElement member; public XElementNullContainer() { member = null; } } 复制代码 Testing for type: System.Xml.Linq.XElement 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f4079346-a4b8-4... 请参见 Deserialized type: System.Xml.Linq.XElement Testing for type: XElementContainer Deserialized type: XElementContainer Testing for type: XElementNullContainer Deserialized type: XElementNullContainer 概念 序列化包含 XElement 或 XDocument 对象的对象图 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/f4079346-a4b8-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用 LINQ to XML 的 WPF 数据绑定 请参见 发送反馈意见 本节提供有关如何将 LINQ to XML 用作 Windows Presentation Foundation (WPF) 应用程序中数据绑定的数据源的信 息。 此方案依赖于 System.Xml.Linq.XAttribute 和 System.Xml.Linq.XElement 的特殊动态属性。 本节也将记述这 些动态属性。 本节内容 参考 请参见 主题 说明 使用 LINQ to XML 的 WPF 数据绑定概述 介绍 System.Xml.Linq 命名空间提供的动态数据绑定功能,并说明如何将这 些功能用作 WPF 中用户界面组件的数据源。 LINQ to XML 动态属性 提供有关由 XAttribute 和 XElement 类公开的动态属性的参考信息。 使用 LINQ to XML 的 WPF 数据绑定示例 提供一个将用户界面组件绑定到 XML 数据源的 WPF 示例。 System.Xml.Linq XElement XAttribute 概念 高级 LINQ to XML 编程 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/356c2cef-3468-4... 全部折叠 代码:C# 使用 LINQ to XML 的 WPF 数据绑定概述 请参见 发送反馈意见 本主题介绍 System.Xml.Linq 命名空间中的动态数据绑定功能。 这些功能可用作 Windows Presentation Foundation (WPF) 中用户界面 (UI) 元素的数据源。 XAML 和 LINQ to XML Windows Presentation Foundation 中的数据绑定 LINQ to XML 类中的动态属性 可扩展应用程序标记语言 (XAML) 是由 Microsoft 创建的 XML 方言,支持 .NET Framework 3.0 技术。 它在 WPF 中用于表示用户界面元素和相关功能,如事件和数据绑定。 在 Windows Workflow Foundation 中,XAML 用于表示程序结构,如程序控制(工作流)。 XAML 使技术的声明性方面与相关的过程性代码分离,从而可 定义更具个性化的程序行为。 XAML 和 LINQ to XML 的交互有两种主要方式: z 由于 XAML 文件是格式良好的 XML,因此可以通过 XML 技术(如 LINQ to XML)查询和操作。 z 由于 LINQ to XML 查询表示数据的源,因此这些查询可用作 WPF UI 元素数据绑定的数据源。 本文档说明第二种情况。 WPF 数据绑定可使 UI 元素将其一个属性与一个数据源相关联。 这种情况的一个简单示例是 Label,其文本 表示用户定义对象中一个公共属性的值。 WPF 数据绑定依赖于下列组件: 依赖项属性是特定于 WPF 的概念,它表示 UI 元素的动态计算的属性。 例如,依赖项属性通常具有默认值或 具有由父元素提供的值。 DependencyProperty 类的实例(而不是支持标准属性的字段)支持这些特殊属性。 有关更多信息,请参见Dependency Properties Overview。 WPF 中的动态数据绑定 默认情况下,仅在初始化目标 UI 元素时,才会发生数据绑定。 这称为“一次性”绑定。 这不能满足多数用 途的需要;通常,数据绑定解决方案要求使用以下方式之一在运行时动态传播更改: z 单向绑定,这种方式会自动传播对一侧所做的更改。 最常见的情况是对源所做的更改会反映在目标中, 但有时需要相反的情况。 z 双向绑定,在这种方式中,对源所做的更改会自动传播到目标,而且对目标的更改也会自动传播到源。 为了进行单向或双向绑定,源必须实现一种更改通知机制,例如通过实现 INotifyPropertyChanged 接口或通 过对支持的每个属性使用 PropertyNameChanged 模式。 有关 WPF 中数据绑定的更多信息,请参见Data Binding。 组件 说明 绑定 目标 要与数据源关联的 UI 元素。 WPF 中的可视元素是从 UIElement 类派生的。 目标 属性 绑定目标的依赖项属性,反映数据绑定源的值。 从中派生 UIElement 的 DependencyObject 类 直接支持依赖项属性。 绑定 源 提供给 UI 元素以便进行显示的一个或多个值的源对象。 WPF 自动支持以下类型作为绑定源: CLR 对象、ADO.NET 数据对象、XML 数据(来自 XPath 或 LINQ to XML 查询)或其他 DependencyObject。 源路 径 绑定源的属性,可解析为要绑定的一个或一组值。 大多数 LINQ to XML 类都不适合作为适当的 WPF 动态数据源: 一些最有用的信息只能通过方法(而不是属 性)提供,并且这些类中的属性不实现更改通知。 为了支持 WPF 数据绑定,LINQ to XML 公开了一组动态属 性。 这些动态属性是特殊的运行时属性,它们重复 XAttribute 和 XElement 类中现有方法和属性的功能。 将这些 属性添加到这些类中只是为了使这些类能够充当 WPF 的动态数据源。 为了满足这一要求,所有这些动态属 性都要实现更改通知。 下一节 LINQ to XML 动态属性中提供有关这些动态属性的详细参考。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3bf80845-891b-4... 请参见 访问动态属性 不能像访问标准属性那样访问 XAttribute 和 XElement 类中的动态属性。 例如,在符合 CLR 的语言(如 C#)中,动态属性不能: z 在编译时直接访问。 动态属性对于编译器和 Visual Studio IntelliSense 是不可见的。 z 在运行时使用 .NET 反射来发现或访问。 即使在运行时,它们也不是基本 CLR 意义上的属性。 在 C# 中,动态属性只能在运行时通过 System.ComponentModel 命名空间提供的功能进行访问。 但相比之下,在 XML 源中,可以通过下面形式的简洁表示法访问动态属性: 这两个类的动态属性或者解析为可以直接使用的值,或者解析为必须与索引一起提供的索引器,以便获取结果 值或值的集合。 后一种语法采用的格式为: 有关更多信息,请参见 LINQ to XML 动态属性。 为了实现 WPF 动态绑定,需要与 System.Windows.Data 命名空间提供的功能(特别是 Binding 类)一起使 用动态属性。 注意: System.Xml.Linq 命名空间的各个类中的很多标准公共属性都可用于一次性数据绑定。 但请记住,在这种 方案下,源和目标都不会动态更新。 复制代码 . 复制代码 .[] 概念 使用 LINQ to XML 的 WPF 数据绑定 LINQ to XML 动态属性 XAML (WPF) Data Binding (WPF) 其他资源 Using Workflow Markup(使用工作流标记) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3bf80845-891b-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 动态属性 请参见 发送反馈意见 本节提供有关 LINQ to XML 中动态属性的参考信息。 具体地说,这些属性由 System.Xml.Linq 命名空间中的 XAttribute 和 XElement 类公开。 如使用 LINQ to XML 的 WPF 数据绑定概述主题中所述,每个动态属性都等效于同一类中的标准公共属性或方法。 多数情况下应使用这些标准成员;动态属性是专门为 LINQ to XML 数据绑定方案提供的。 有关这些类的标准成员的 更多信息,请参见 XAttribute 和 XElement 参考主题。 就其解析值而论,本节中的动态属性可分为两类: z 解析为单个值的简单动态属性,如 XAttribute 和 XElement 类中的 Value 属性。 z 解析为索引器类型的索引值,如 XElement 的 Elements 和 Descendants 属性。 对于要解析为所需值或集合的 索引器类型,必须为其传递展开名称参数。 返回 IEnumerable(T) 类型索引值的所有动态属性都使用延迟执行。 有关延迟执行的更多信息,请参见 The Three Parts of a LINQ Query。 本节内容 参考 请参见 主题 说明 XAttribute 类动态属性 提供有关由 XAttribute 类公开的动态属性的详细信息。 XElement 类动态属性 提供有关由 XElement 类公开的动态属性的详细信息。 System.Xml.Linq System.Xml.Linq.XElement System.Xml.Linq.XAttribute 概念 使用 LINQ to XML 的 WPF 数据绑定 使用 LINQ to XML 的 WPF 数据绑定概述 The Three Parts of a LINQ Query 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/0455f47c-4a68-4f... 全部折叠 代码:C# 语言集成查询 (LINQ) XAttribute 类动态属性 请参见 发送反馈意见 本节说明 System.Xml.Linq.XAttribute 的动态属性。 本节内容 请参见 主题 说明 Value(XAttribute 动态属性) 获取或设置 XML 属性的值。 概念 LINQ to XML 动态属性 XElement 类动态属性 参考 System.Xml.Linq.XAttribute 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/7394cef0-55e9-4e... 全部折叠 代码:C# 语言集成查询 (LINQ) Value(XAttribute 动态属性) 请参见 发送反馈意见 获取或设置 XML 属性的值。 属性值/返回值 异常 备注 请参见 attrib.Value 包含此属性的值的 String。 异常类型 条件 ArgumentNullException 设置时,value 为 null。 此属性等效于 System.Xml.Linq.XAttribute 类的 Value 属性,但此动态属性还支持更改通知。 概念 XAttribute 类动态属性 属性 (Attribute)(XElement 动态属性 (Property)) 参考 XAttribute.Value 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/019733d2-e050-4... 全部折叠 代码:C# 语言集成查询 (LINQ) XElement 类动态属性 请参见 发送反馈意见 本节介绍 XElemen[t] 类的动态属性。 本节内容 请参见 主题 说明 属性 (Attribute)(XElement 动态属性 (Property)) 获取一个索引器,用于检索与指定扩展名对应的属性。 元素(XElement 动态属性) 获取一个索引器,用于检索与指定扩展名对应的子元素。 Elements(XElement 动态属性) 获取一个索引器,用于检索与指定扩展名匹配的当前元素的 子元素。 Descendants(XElement 动态属性) 获取一个索引器,用于检索与指定扩展名匹配的当前元素的 所有子代元素。 Value(XElement 动态属性) 获取或设置一个元素的内容。 Xml(XElement 动态属性) 获取元素的未格式化的 XML 表示形式。 概念 LINQ to XML 动态属性 XAttribute 类动态属性 参考 System.Xml.Linq.XElement 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/a9795dba-2afc-45... 全部折叠 代码:C# 语言集成查询 (LINQ) 属性 (Attribute)(XElement 动态属性 (Property)) 请参见 发送反馈意见 获取一个索引器,该索引器用于检索与指定扩展名对应的属性实例。 属性值/返回值 备注 请参见 elem.Attribute[{namespaceName}attribName] 一个类型为 XAttribute Item(String expandedName) 的索引器。 此索引器获取指定属性的扩展名,然后 返回相应的 XAttribute,如果没有具有指定名称的属性,则返回 null。 此属性等效于 System.Xml.Linq.XElement 类的 Attribute 方法。 概念 XElement 类动态属性 Value(XAttribute 动态属性) 参考 XElement.Attribute 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/8440fc7d-b3b4-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 元素(XElement 动态属性) 请参见 发送反馈意见 获取一个索引器,用于检索与指定扩展名对应的子元素实例。 属性值/返回值 备注 请参见 elem.Element[{namespaceName}localName] 一个类型为 XElement Item(String expandedName) 的索引器。 此索引器获取扩展名参数并返回相应的 XElement,或者如果没有具有指定名称的元素,则返回 null。 此属性等效于 System.Xml.Linq.XContainer 类的 Element 方法。 概念 XElement 类动态属性 Elements(XElement 动态属性) 参考 XContainer.Element 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/c6c25b8d-a1da-4... 全部折叠 代码:C# 语言集成查询 (LINQ) Elements(XElement 动态属性) 请参见 发送反馈意见 获取一个索引器,用于检索与指定扩展名匹配的当前元素的子元素。 属性值/返回值 备注 请参见 elem.Elements[{namespaceName}localName] 一个类型为 IEnumerable Item(String expandedName) 的索引器。 此索引器获取所需子元素 的扩展名,并返回 IEnumerable 集合中的匹配子元素。 此属性等效于 XContainer 类的 XContainer.Elements(XName) 方法。 返回集合中的元素按 XML 源文档顺序排列。 此属性使用延迟执行。 概念 XElement 类动态属性 元素(XElement 动态属性) Descendants(XElement 动态属性) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3d5737f2-d2ed-4... 全部折叠 代码:C# 语言集成查询 (LINQ) Descendants(XElement 动态属性) 请参见 发送反馈意见 获取一个索引器,用于检索与指定扩展名匹配的当前元素的所有子代元素。 属性值/返回值 备注 请参见 elem.Descendants[{namespaceName}localName] 一个类型为 IEnumerable Item(String expandedName) 的索引器。 此索引器获取指定子代元 素的扩展名,并返回 IEnumerable 集合中的匹配子元素。 此属性等效于 XContainer 类的 XContainer.Descendants(XName) 方法。 返回集合中的元素按 XML 源文档顺序排列。 此属性使用延迟执行。 概念 XElement 类动态属性 Elements(XElement 动态属性) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/9611d00f-23bf-4... 全部折叠 代码:C# 语言集成查询 (LINQ) Value(XElement 动态属性) 请参见 发送反馈意见 获取或设置元素的内容。 属性值/返回值 备注 请参见 elem.Value 一个 String,表示元素的串联内容。 此属性等效于 System.Xml.Linq.XElement 类的 Value 属性,但此动态属性还支持更改通知。 概念 XElement 类动态属性 Xml(XElement 动态属性) 参考 XElement.Value 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/b30e770d-9646-4... 全部折叠 代码:C# 语言集成查询 (LINQ) Xml(XElement 动态属性) 请参见 发送反馈意见 获取元素的非格式化 XML 内容。 属性值/返回值 备注 请参见 elem.Xml 表示元素的非格式化 XML 内容的 String。 此属性等效于 System.Xml.Linq.XNode 类的 ToString(SaveOptions) 方法,其 SaveOptions 参数设置为 DisableFormatting。 概念 XElement 类动态属性 Value(XElement 动态属性) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/69ab2a33-4fe7-4c... 全部折叠 代码:C# 语言集成查询 (LINQ) 使用 LINQ to XML 的 WPF 数据绑定示例 请参见 发送反馈意见 本节提供一个 Windows Presentation Foundation (WPF) 示例,该示例将用户界面组件绑定到嵌入式 XML 数据源。 此示例(以及包含此示例的 Visual Studio 项目)的名称是“LinqToXmlDataBinding”。 本节内容 请参见 主题 说明 如何: 生成并运行 LinqToXmlDataBinding 示例 以分步说明的形式,介绍如何创建、填充和生成此示例的 Visual Studio 项目。 演练: LinqToXmlDataBinding 示例 介绍该项目的主要源文件,并对在代码中将 LINQ to XML 用于 数据绑定的方式进行说明。 概念 使用 LINQ to XML 的 WPF 数据绑定 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/8e90b252-646e-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 如何: 生成并运行 LinqToXmlDataBinding 示例 请参见 发送反馈意见 本主题演示如何创建和生成 LinqToXmlDataBinding Visual Studio 项目以及如何运行生成的 LinqToXmlDataBinding Windows Presentation Foundation (WPF) 示例程序。 有关使用 Visual Studio 创建项目的更多信息,请参见 Application Development in Visual Studio。 创建和填充项目 运行程序 请参见 创建起始项目 1. 启动 Visual Studio 并创建一个名为 LinqToXmlDataBinding 的 C# WPF 应用程序。 该项目必须使用 .NET Framework 3.5(或更高版本)。 2. 为以下 .NET 程序集添加项目引用(如果尚不存在): z System.Data z System.Data.DataSetExtensions z System.Xml z System.Xml.Linq 3. 通过按“Ctnrl+Shift+B”生成解决方案,然后按“F5”运行该解决方案。 该项目应正确编译而不出错,并作为一般 WPF 应用程序运行。 对项目添加自定义代码 1. 在解决方案资源管理器中,将源文件 Window1.xaml 重命名为 L2XDBForm.xaml。 依赖源文件 Window1.xaml.cs 应该会自动重命名为 L2XDBForm.xaml.cs。 2. 用 L2DBForm.xaml 源代码主题中的代码节替换 L2XDBForm.xaml 文件中的源代码。 (使用 XAML 源视图来处理此 文件。) 3. 同样,用 L2DBForm.xaml.cs 源代码中的代码替换 L2XDBForm.xaml.cs 中的源代码。 4. 在 App.xaml 文件中,用“L2XDBForm.xaml”替换“Window1.xaml”字符串的所有匹配项。 5. 按“Ctrl+Shift+B”生成解决方案。 LinqToXmlDataBinding 程序可以让用户查看和操作以嵌入式 XML 元素形式存储的书籍的列表。 运行程序并查看书籍列表 1. 按“F5”(“启动调试”)或“Ctrl+F5”(“开始执行(不调试)”)运行 LinqToXmlDataBinding。 应显示标题为 “WPF Data Binding using LINQ to XML”(使用 LINQ to XML 的 WPF 数据绑定)的程序窗口。 2. 请注意,UI 的顶部区域将显示表示书籍列表的原始“XML”。 它使用 WPF TextBlock 控件来显示,该控件不启用通 过鼠标或键盘交互。 3. 标记为“Book List”(书籍列表)的第二个垂直区域以排序的纯文本列表形式显示书籍。 它使用启用通过鼠标或键 盘进行选择的 ListBox 控件。 在列表中添加和删除书籍 1. 若要从列表中删除现有书籍,请在“Book List”(书籍列表)区域选择该书籍,然后单击“Remove Selected Book”(移除所选书籍)按钮。 请注意,这会从书籍列表和原始 XML 源列表中移除该书籍条目。 2. 若要向列表中添加新书籍,请向最后一个区域“Add New Book”(添加新书籍)的“ID”和“Value”(值) TextBox 控件中输入值,然后单击“Add Book”(添加书籍)按钮。 请注意,这会向书籍列表和 XML 列表中追加 该书籍。 此程序不验证输入值。 编辑现有书籍条目 1. 在第二个“Book List”(书籍列表)区域中选择书籍条目。 其当前值应该显示在第三个区域“Edit Selected Book”(编辑所选书籍)中。 2. 使用键盘编辑值。 只要任一 TextBox 控件失去焦点,更改就会自动传播到 XML 源和书籍列表。 概念 使用 LINQ to XML 的 WPF 数据绑定示例 演练: LinqToXmlDataBinding 示例 Application Development in Visual Studio 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3943deaf-80e2-4... 全部折叠 代码:C# 语言集成查询 (LINQ) 演练: LinqToXmlDataBinding 示例 请参见 发送反馈意见 本演练介绍 LinqToXmlDataBinding 示例,解释它的两个主要源文件 L2DBForm.xaml 和 L2DBForm.xaml.cs 的一些 更值得关注的内容。 先决条件 备注 本节内容 请参见 阅读此演练之前,强烈建议您按照如何: 生成并运行 LinqToXmlDataBinding 示例中的描述生成并运行 LinqToXmlDataBinding 程序。 LinqToXmlDataBinding 程序是一个 Windows Presentation Foundation (WPF) 应用程序,由 C# 和 XAML 源文 件组成。 它包含定义书籍列表的嵌入式 XML 文档,允许用户查看、添加、删除和编辑这些项。 它由以下两 个主要源文件组成: z L2DBForm.xaml 包含主窗口的用户界面 (UI) 的 XAML 声明代码。 还包含为书籍列表定义数据提供程序 和嵌入式 XML 文档的窗口资源部分。 z L2DBForm.xaml.cs 包含与用户界面关联的初始化和事件处理方法。 主窗口分为以下四个垂直用户界面部分: z “XML”显示嵌入式书籍列表的原始 XML 源。 z “书籍列表”[Book List]以标准文本形式显示书籍项,允许用户选择和删除各项。 z “编辑所选书籍”[Edit Selected Book]允许用户编辑与当前所选书籍项关联的值。 z “添加新书”[Add New Book]允许根据用户输入的值创建新书。 主题 说明 L2DBForm.xaml 源代码 包含文件 L2DBForm.xaml 中的 XAML 代码的内容和描述。 L2DBForm.xaml.cs 源代码 包含文件 L2DBForm.xaml.cs 中的 C# 源代码的内容和描述。 概念 使用 LINQ to XML 的 WPF 数据绑定示例 如何: 生成并运行 LinqToXmlDataBinding 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/aedf42e8-896c-48... 全部折叠 代码:C# 语言集成查询 (LINQ) L2DBForm.xaml 源代码 请参见 发送反馈意见 本主题包含并说明使用 LINQ to XML 的 WPF 数据绑定示例的 XAML 源文件 L2DBForm.xaml。 总体 UI 结构 窗口资源区域 数据绑定代码 示例 和典型的 WPF 项目一样,此文件包含一个父元素,该元素是一个与 LinqToXmlDataBinding 命名空间中的派生类 L2XDBFrom 相关联的 Window XML 元素。 客户端区域包含在具有浅蓝色背景的 StackPanel 中。 此面板包含四个 DockPanel UI 区域,由 Separator 条分隔。 上一个主题的备注中说明了这些区域的用途。 每个区域都包含一个标识该区域的标签。 在前两个区域中,此标签通过使用 LayoutTransform 旋转 90 度。 该区域的其余部分包含对应于该区域用途的 UI 元素: 文本块、文本框、按钮等。 有时使用子 StackPanel 来对齐这 些子控件。 在第 9 行上的 开始标记指示窗口资源区域的开始。 它在第 35 行上以结束标记结束。 标记跨越第 11 至第 25 行,声明一个名为 LoadedBooks 的 ObjectDataProvider,它使用 XElement 作为源。 此 XElement 是通过分析嵌入的 XML 文档(一个 CDATA 元素)来初始化的。 请注意,在 声明嵌入的 XML 文档以及分析该文档时将保留空白。 之所以会这样是因为用于显示原始 XML 的 TextBlock 控件没有专门的 XML 格式化功能。 最后,第 28 行至第 34 行定义一个名为 BookTemplate 的 DataTemplate。此模板将用于显示“Book List”(书籍列表)UI 区域中的条目。 它使用数据绑定和 LINQ to XML 动态属性通过下面的赋值来检索书籍 ID 和书名: 复制代码 Text="{Binding Path=Attribute[id].Value}"Text="{Binding Path=Value}" 除 DataTemplate 元素外,本文件中的许多其他位置也使用数据绑定。 在第 38 行的 开始标记中,此面板的 DataContext 属性设置为 LoadedBooks 数据提供程序。 这使名为 tbRawXml 的 TextBlock(第 46 行)能够通过绑定到此数据提供程序的 Xml 属性来显示原始 XML: 从第 58 行至第 62 行,“Book List”(书籍列表)UI 区域中的 ListBox 将其显示项的模板设置为在窗口资源区域中定义的 BookTemplate: 然后,第 59 行至第 62 行将书籍的实际值绑定到此列表框: 在第三个 UI 区域“Edit Selected Book”(编辑所选书籍)中,首先将父 StackPanel 的 DataContext 绑定到“Book List”(书籍列表)UI 区域中的当前所选项(第 82 行): 然后使用双向数据绑定,使书籍元素的当前值显示在此面板的两个文本框中并从中进行更新。 数据绑定到动态属性类似于在 BookTemplate 数据模板中使用数据: 最后一个 UI 区域“Add New Book”(添加新书籍)不在其 XAML 代码中使用数据绑定;但可以在 L2DBForm.xaml.cs 文件的事件处理代码中找到这样的代码。 复制代码 DataContext="{Binding Source={StaticResource LoadedBooks}} 复制代码 Text="{Binding Path=Xml}" 复制代码 ItemTemplate ="{StaticResource BookTemplate}" 复制代码 复制代码 DataContext="{Binding ElementName=lbBooks, Path=SelectedItem}" 复制代码 Text="{Binding Path=Attribute[id].Value}"...Text="{Binding Path=Value}" 说明 代码 注意: 建议您将下面的代码复制到代码编辑器(如 Visual Studio 中的 C# 源代码编辑器)中,以便更易于跟踪行号。 Xml 复制代码 book zero book one book two book three ]]> PreserveWhitespace Changes are live! 概念 演练: LinqToXmlDataBinding 示例 L2DBForm.xaml.cs 源代码 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/624e96d4-6d27-4... 全部折叠 代码:C# 语言集成查询 (LINQ) L2DBForm.xaml.cs 源代码 请参见 发送反馈意见 本主题包含文件 L2DBForm.xaml.cs 中 C# 源代码的内容和说明。 本文件中包含的 L2XDBForm 分部类可分为三个逻辑 区域: 数据成员、OnRemove 和 OnAddBook 按钮单击事件处理程序。 数据成员 OnAddBook 事件处理程序 OnRemove 事件处理程序 示例 使用两个私有数据成员将此类与 L2DBForm.xaml 中使用的窗口资源相关联。 z 命名空间变量 myBooks 初始化为 "http://www.mybooks.com"。 z 用下面的行将构造函数中的成员 bookList 初始化为 L2DBForm.xaml 中的 CDATA 字符串: 复制代码 bookList = (XElement)((ObjectDataProvider)Resources["LoadedBooks"]).Data; 此方法包含下面三个语句: z 第一个条件语句用于输入验证。 z 第二个语句从用户在“Add New Book”(添加新书籍)用户接口 (UI) 区域中输入的字符串值创建新的 XElement。 z 最后一个语句将此新书籍元素添加到 L2DBForm.xaml 中的数据提供程序。 因此,动态数据绑定将用此新项自 动更新 UI;不需要用户提供额外的代码。 由于两个原因,OnRemove 处理程序比 OnAddBook 处理程序更复杂。 首先,由于原始 XML 包含保留的空白,因此 还必须与书籍条目一起移除匹配的换行符。 其次,出于方便,对所选项进行的选择会重置为列表中以前的选择。 但是,移除所选书籍项的核心工作仅通过两个语句完成: z 首先,检索与列表框中当前所选项相关联的书籍元素: z 然后,从数据提供程序中删除此元素: 此外,动态数据绑定将确保自动更新程序的 UI。 复制代码 XElement selBook = (XElement)lbBooks.SelectedItem; 复制代码 selBook.Remove(); 代码 C# 复制代码 using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Xml; using System.Xml.Linq; namespace LinqToXmlDataBinding { /// /// Interaction logic for L2XDBForm.xaml /// public partial class L2XDBForm : System.Windows.Window { XNamespace mybooks = "http://www.mybooks.com"; XElement bookList; 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5a40dad3-6763-4... 请参见 注释 有关这些处理程序的关联 XAML 源,请参见 L2DBForm.xaml 源代码。 public L2XDBForm() { InitializeComponent(); bookList = (XElement)((ObjectDataProvider)Resources["LoadedBooks"]).Data; } void OnRemoveBook(object sender, EventArgs e) { int index = lbBooks.SelectedIndex; if (index < 0) return; XElement selBook = (XElement)lbBooks.SelectedItem; //Get next node before removing element. XNode nextNode = selBook.NextNode; selBook.Remove(); //Remove any matching newline node. if (nextNode != null && nextNode.ToString().Trim().Equals("")) { nextNode.Remove(); } //Set selected item. if (lbBooks.Items.Count > 0) { lbBooks.SelectedItem = lbBooks.Items[index > 0 ? index - 1 : 0]; } } void OnAddBook(object sender, EventArgs e) { if (String.IsNullOrEmpty(tbAddID.Text) || String.IsNullOrEmpty(tbAddValue.Text)) { MessageBox.Show("Please supply both a Book ID and a Value!", "Entry Error!"); return; } XElement newBook = new XElement( mybooks + "book", new XAttribute("id", tbAddID.Text), tbAddValue.Text); bookList.Add(" ", newBook, "\r\n"); } } } 概念 演练: LinqToXmlDataBinding 示例 L2DBForm.xaml 源代码 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/5a40dad3-6763-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ to XML 安全性 请参见 发送反馈意见 本主题说明与 LINQ to XML 相关的安全问题。 此外,它还提供减轻安全隐患的一些指导。 LINQ to XML 安全性概述 缓解 XML、XSD、XPath 和 XSLT 攻击 LINQ to XML 安全问题 在旨在提高编程方便性和使服务器端应用程序具有严格安全要求两者之间,LINQ to XML 更倾向于前者。 多 数 XML 方案均涉及处理受信任的 XML 文档,而不涉及处理上载到服务器的不受信任的 XML 文档。 LINQ to XML 针对这些方案进行了优化。 如果您必须处理来自未知源的不受信任的数据,Microsoft 建议您使用 XmlReader 类的一个实例,并将该实例 配置为筛除已知 XML 拒绝服务 (DoS) 攻击。 如果您已配置 XmlReader 以缓解拒绝服务攻击,则可以使用该读取器填充 LINQ to XML 树,同时仍可受益于 LINQ to XML 所提供的高编程效率。 许多缓解技术均涉及创建配置为可缓解安全问题的读取器,然后通过配 置的读取器实例化 XML 树。 由于文档不受大小、深度、元素名称大小等的限制,因此 XML 从本质上就容易受到拒绝服务攻击。 不管使用 哪个组件处理 XML,都应该始终准备好在应用程序域使用过多资源的情况下回收应用程序域。 LINQ to XML 是基于 XmlReader 和 XmlWriter 生成的。 LINQ to XML 通过 System.Xml.Schema 和 System.Xml.XPath 命名空间中的扩展方法支持 XSD 和 XPath。 在结合 LINQ to XML 使用 XmlReader、 XPathNavigator 和 XmlWriter 类时,可以调用 XSLT 来转换 XML 树。 如果在不足够安全的环境中操作,则可能会遇到许多与 XML 及 System.Xml、System.Xml.Schema、 System.Xml.XPath 和 System.Xml.Xsl 中的类的使用相关的安全问题。 这些问题包括但不限于以下问题: z XSD、XPath 和 XSLT 是基于字符串的语言,您可以使用此语言指定消耗很多时间和内存的操作。 从不受 信任的源获取 XSD、XPath 或 XSLT 字符串的应用程序程序员应当负责验证这些字符串以确保其中不包含 恶意内容,或监视这些字符串并尽量避免计算这些字符串时消耗过多的系统资源。 z XSD 架构(包括内联架构)从本质上就容易受到拒绝服务攻击,因此不应接受来自不受信任的源的架构。 z XSD 和 XSLT 可能包括对其他文件的引用,这种引用可能导致跨区和跨域攻击。 z DTD 中的外部实体可能会导致跨区和跨域攻击。 z DTD 容易受到拒绝服务攻击。 z 异常深的 XML 文档可能引起拒绝服务问题;您可能需要限制 XML 文档的深度。 z 不接受不受信任的程序集中的支持组件,如 NameTable、XmlNamespaceManager 和 XmlResolver 对象。 z 分块区读取数据以缓解大文档攻击。 z XSLT 样式表中的脚本块易于受到多种攻击。 z 构造动态 XPath 表达式之前需仔细验证这些表达式。 有关这些攻击及其缓解方法的更多信息,请参见安全性和 System.Xml 应用程序。 本主题中介绍的安全问题不区分顺序。 所有问题都很重要,都应得到相应解决。 成功的特权提升攻击可使恶意程序集能够更好地控制其环境。 成功的特权提升攻击可能导致数据泄露、拒绝 服务等。 应用程序不应该向未经授权查看数据的用户泄露数据。 拒绝服务攻击会导致 XML 分析器或 LINQ to XML 消耗过多的内存或 CPU 时间。 拒绝服务攻击的严重性可视 为比特权提升攻击或数据泄露攻击的严重性低。 但在服务器需要处理来自不受信任的源的 XML 文档的方案 中,拒绝服务攻击很重要。 异常和错误消息可能会泄露数据 错误说明中可能会显示数据,如正在转换的数据、文件名或实现详细信息。 不应向不受信任的调用方公开错 误消息。 您应该捕捉所有错误并用自己的自定义错误消息报告错误。 不要在事件处理程序中调用 CodeAccessPermissions.Assert 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3abd3fcf-0ec0-40... 请参见 程序集可具有较低或较高的权限。 程序集的权限越高,其对计算机及其环境的控制程度也就越高。 如果具有较高权限的程序集中的代码在事件处理程序中调用 CodeAccessPermission.Assert,然后将 XML 树传 递给具有受限制权限的恶意程序集,则恶意程序集可能引发事件。 由于该事件运行具有较高权限的程序集中 的代码,因此恶意程序集将使用提升的特权运行。 Microsoft 建议您永远不要在事件处理程序中调用 CodeAccessPermission.Assert。 DTD 不安全 DTD 中的实体在本质上就不安全。 包含 DTD 的恶意 XML 文档可使分析器使用全部内存和 CPU 时间,从而 导致拒绝服务攻击。 因此,在 LINQ to XML 中,默认情况下关闭 DTD 处理。 您不应接受来自不受信任源的 DTD。 接受来自不受信任源的 DTD 的一个示例是:允许 Web 用户上载引用 DTD 和 DTD 文件的 XML 文件的 Web 应用程序。 在验证该文件时,恶意 DTD 会在您的服务器上执行拒绝服务攻击。 接受来自不受信任源的 DTD 的另一个示例是:引用网络共享上也允许匿名 FTP 访问的 DTD。 避免分配过多的缓冲区 应用程序开发人员应该知道,过大的数据源可能导致资源耗尽和拒绝服务攻击。 如果恶意用户提交或上载非常大的 XML 文档,则可能导致 LINQ to XML 使用过多的系统资源。 这可能构成 拒绝服务攻击。 为防止这种情况的发生,可以设置 XmlReaderSettings.MaxCharactersInDocument 属性并创 建一个读取器,然后限制读取器可以加载的文档大小。 然后使用该读取器创建 XML 树。 例如,如果您知道来自不受信任源的 XML 文档的预计最大大小不会超过 50K 字节,则将 XmlReaderSettings.MaxCharactersInDocument 设置为 100,000。 这不会妨碍您处理 XML 文档,同时还可在 要上载的文档会使用大量内存的情况下缓解拒绝服务威胁。 避免过度扩展实体 使用 DTD 时的一种已知拒绝服务攻击是导致过度扩展实体的文档造成的。 为防止这种情况的发生,可以设 置 XmlReaderSettings.MaxCharactersFromEntities 属性并创建一个读取器,然后限制从实体扩展中生成的字符 数。 然后使用该读取器创建 XML 树。 限制 XML 层次结构的深度 在提交具有过深层次结构的文档时,会发生一种可能的拒绝服务攻击。 为了防止这种情况的发生,可以将 XmlReader 包装在您自己的可计算元素深度的类中。 如果深度超过预设的合理等级,您可以终止处理恶意文 档。 防止不受信任的 XmlReader 或 XmlWriter 实现 管理员应验证任何外部提供的 XmlReader 或 XmlWriter 实现都要具有强名称且已经在计算机配置中注册。 这 可防止加载伪装成读取器或编写器的恶意代码。 定期释放引用 XName 的对象 为防止某些类型的攻击,应用程序程序员应该定期释放引用应用程序域中的 XName 对象的所有对象。 防止随机 XML 名称 从不受信任的源中获取数据的应用程序应该考虑使用包装在自定义代码中的 XmlReader 来检查是否可能会出 现随机 XML 名称和命名空间。 如果检测到这样的随机 XML 名称和命名空间,则应用程序可以终止处理恶意 文档。 您可能需要将任何给定命名空间中的名称数目(包括不在命名空间中的名称)限制为合理的极限。 通过共享 LINQ to XML 树的软件组件可以访问批注 LINQ to XML 可用于生成处理管道,其中的不同应用程序组件可分别加载、验证、查询、转换、更新和保存在 组件之间作为 XML 树传递的 XML 数据。 这有助于优化性能,因为仅在管道末端消耗加载对象并将对象序列 化为 XML 文本的系统资源。 但开发人员必须知道,由一个组件创建的所有批注和事件处理程序都可以由其他 组件访问。 如果组件的信任级别不同,这可能会造成很多安全漏洞。 若要在低信任级别的组件之间生成安全 管道,在将数据传递给不受信任的组件之前,您必须将 LINQ to XML 对象序列化为 XML 文本。 某些安全功能是由公共语言运行库 (CLR) 提供的。 例如,不包括私有类的组件无法访问由该类键控的批注。 但是,不能读取批注的组件却可以删除该批注。 这可能用作篡改攻击。 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3abd3fcf-0ec0-40... 概念 编程指南 (LINQ to XML) 安全性和 System.Xml 应用程序 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/3abd3fcf-0ec0-40... 全部折叠 代码:C# 语言集成查询 (LINQ) 示例 XML 文档 (LINQ to XML) 请参见 发送反馈意见 整个 LINQ to XML 文档的代码示例和代码段中均使用下面的示例文件。 本节内容 请参见 注意: 文档中的示例公司、组织、产品、域名、电子邮件地址、徽标、人物、地点和事件纯属虚构。 并不有意联系或 暗示任何实际的公司、组织、产品、域名、电子邮件地址、徽标、人物、地点或事件。 主题 说明 示例 XML 文件: 典型采购订单 (LINQ to XML) 包含一个典型采购订单的 XML 文档。 示例 XML 文件: 命名空间中的典型 采购单 处于某一命名空间中的包含一个典型采购订单的 XML 文档。 示例 XML 文件: 多个采购订单 (LINQ to XML) 包含多个采购订单的 XML 文档。 示例 XML 文件: 命名空间中的多个 采购单 处于某一命名空间中的包含多个采购订单的 XML 文档。 示例 XML 文件: 测试配置 (LINQ to XML) 包含一些伪测试配置数据的 XML 文档。 示例 XML 文件: 命名空间中的测试 配置 处于某一命名空间中的包含一些伪测试配置数据的 XML 文档。 示例 XML 文件: 客户和订单 (LINQ to XML) 包含客户和订单的 XML 文档。 示例 XSD 文件: 客户和订单 用于验证示例 XML 文件: 客户和订单 (LINQ to XML) 的 Xml 架构定义 (XSD)。 示例 XML 文件: 命名空间中的客户 和订单 处于某一命名空间中的包含客户和订单的 XML 文档。 示例 XML 文件: 数值数据 (LINQ to XML) 包含适合于求和和分组的数据的 XML 文档。 示例 XML 文件: 命名空间中的数值 数据 处于某一命名空间中的包含适合于求和和分组的数据的 XML 文 档。 示例 XML 文件: 图书 (LINQ to XML) 包含书籍目录的 XML 文档。 示例 XML 文件: 合并采购单 表示包含处于不同命名空间中的采购订单的 XML 文档。 概念 编程指南 (LINQ to XML) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqxml/html/26b36d49-6857-4... 全部折叠 代码:C# 语言集成查询 (LINQ) LINQ C# 示例 发送反馈意见 本地驱动器的 <驱动器>:\Program Files\Microsoft Visual Studio 9.0\Sample 文件夹下和以下联机位置上提供了演示 C# 中的 LINQ 技术的示例: Visual Studio Visual C# 开发人员中心 还联机提供了打开和运行示例的说明。 本节内容 相关章节 示例查询 提供对 LINQ to SQL to Objects、LINQ、LINQ to XML 和 LINQ to DataSet 执行查询操作的示例。 数据示例 用于支持各种 LINQ 示例中演示的许多方案。 NorthwindMapping 示例 生成由某些其他示例使用的对象关系映射文件。 简单的 LINQ to Objects 示例 演示如何查询内存中的集合。 动态查询示例 演示如何在运行时动态创建 LINQ 查询。 表达式目录树可视化工具示例 提供可在 Visual Studio 调试器中运行以查看表达式目录树的内容的可视化工具的工作实现。 LINQ to Northwind 示例 提供使用 LINQ to SQL 查询数据库的示例。 LINQ 查询可视化工具示例 您可以在 Visual Studio 内的调试模式下运行以测试表达式目录树可视化工具的小应用程序。 对象转储程序示例 一个库,您可以将其添加到解决方案以输出 LINQ 查询结果来进行测试。 “将 XML 作为 LINQ 粘贴”示例 演示一个 Visual Studio 外接程序,它将 XML 代码示例转换为 C# 代码,该代码在 LINQ to XML API 中生成等效的 XML。 Reflector 示例 生成一个可概述给定程序集的公共 API 的 HTML 文档。 RSS 示例 充当聚合几个 RSS 源的小 Web 服务器。 简单的 Lambda 示例 提供简单的 lambda 表达式的示例。 Windows 窗体数据绑定示例 演示如何在 Windows 窗体数据绑定方案中使用 LINQ。 LINQ to XML 示例简介 演示有关 LINQ to XML 的关键概念。 XQuery 示例 演示如何使用 LINQ to XML 来解决 XQuery 标准中的用例。 LINQ to XML 数据绑定示例 演示到 Windows Presentation Foundation (WPF) 功能的 LINQ to XML 数据绑定。 Visual Basic 中的 LINQ 示例应用程序 有关 Visual Basic LINQ 示例的说明信息。 白皮书 提供指向提供有关 LINQ 的其他信息的白皮书的链接。 语言集成查询 (LINQ) 包括 LINQ 的一般说明和指向相关技术的链接。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsamples/html/104231c9-293... 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsamples/html/104231c9-293...
还剩379页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

uncommon

贡献于2013-03-21

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!