本书翻译目的为个人学习和知识共享,其版权属原作者所有,如有侵权,请告知本人,本人将立即对发帖采取处理。
允许转载,但转载时请注明本版权声明信息,禁止用于商业用途!
博客园:韩现龙
Introducing to Microsoft LINQ目录
Visual Basic9.0有一些在C#3.0中没有的特性。这些特性有一部分和LINQ相关,只有在使用LINQ to XML的时候对于XML的支持才有很大的不同。动态的接口和动态的标志符在Visual Basic9.0中对于LINQ的支持非必需的。
XML支持(XML Support)
Visual Basic9.0是实现了对LINQ to XML的特殊语法支持的一种语言,包括XML文本(XML Literals)和对XML的延迟绑定。为了说明这一点,我们将使用作为LINQ to XML的一部分的一些类:XDocument,XElement,和XAttribute.在第六章中我们将对其进行更详细的讲解。为了描述对XML的支持,知道这些类分别代表XML 文档,元素和属性即可了。
XML文本(XML Literals)
在Visual Basic9.0中,XML Literal被认为是一个表达式。如果你想将一个值赋给一个代表XML树的对象的话,写如Listing 3-23那样的代码即可了:
Listing 3-23: XML literal used as a constant
1Dim ourBook As XElement
2ourBook = _
3 <Book Title="Introducing LINQ">
4 <Author>Marco Russo</Author>
5 <Author>Paolo Pialorsi</Author>
6 </Book>
7
1Dim book As XElement
2book = New XElement("Book", _
3 New XAttribute("Title", "Introducing LINQ"), _
4 New XElement("Author", "Marco Russo"), _
5 New XElement("Author", "Paolo Pialorsi"))
要点 正如我们前面所说的,XML文本在Visual Basic9.0中是表达式。在定义多行这些表达式时不需要续行符。更为重要的是,无论是下划线还是注释均会认为是XML文本的一部分,而不会认为是续行符或者是XML的注释。这对于Visual Basic来说是一个很大的例外。表达式的末尾是一个和初始的tag相对应的闭合的tag。
通常情况下,我们通过将初始的任务放进声明中的方式去推断变量的类型。在下面的任务中我们还可以看到将XML文本定义到一行中:
1Dim book = <Book Title="Introducing LINQ"></Book>
理解XML文本被编译器转换为方法的调用这一点很重要。编译器需要合法的XML语法。非法的XML文本将会产生编译错误。在Listing3-24中,赋值给a变量的XML文本有一个非法的title属性(缺少一个"),赋值给b的缺少一个闭合标签(</Book>):
Listing 3-24: Invalid XML literals
1Dim a = <Book Title="Invalid Title></Book> ' Error
2Dim b = <Book Title="Good Title"><Book> ' Error
3
Visual Basic 9.0对XML文本有影响,因此它启用了对其他表达式的调用。也就是说,XML文本可以在运行时被计算而不仅仅是在代码中的常量。例如,假设我们想动态的为一本书生成XML文本,并且用一个字符串来指定书名属性。在这种情况下,我们需要用一个常用的Visual Basic9.0中的表达式<%=和%>标签来“打断”XML文本。Listing 3-25展示了如何用常用的Visual Basic9.0的表达式对属性和元素的赋值:
Listing 3-25: XML literal in a dynamic expression
2Dim author1 = "Marco Russo"
3Dim author2 = "Paolo Pialorsi"
4Dim book = _
5 <Book Title=<%= bookTitle %>>
6 <Author><%= author1 %></Author>
7 <Author><%= author2 %></Author>
8 </Book>
9
警告 在<%=之后和%>之前请留个空格,否则编译器就无法正确的理解这些表达式。
<%=和%>标签为XML文本定义了一个“空缺“用来嵌套表达式,在运行时计算该表达式的值并且用它来替代那个“空缺”。这些“空缺”就是和XElement,XAttribute,或者XDocument构造器相等价的占位符。我们可以将在上下文中合法的表达式放进“空缺”中。
Listing 3-26: Dynamic XML tags in an XML literal
2Dim author1 = "Marco Russo"
3Dim author2 = "Paolo Pialorsi"
4Dim tagBook = "Book"
5Dim attrName = "Title"
6Dim tagAuthors = "Authors"
7Dim book = _
8 <<%= tagBook %> <%= attrName %>=<%= bookTitle %>>
9 <<%= tagAuthors %>><%= author1 & ", " & author2 %></>
10 </>
笔者认为,在Listing3-26的代码并不是使用XML文本的最好的方法。以 尖括号和“空缺”的方式将代码插入代码中对于代码的可读性会有一定的影响。以Listing 3-27的方式去定义book可能会更清晰明了(为简明起来,我们跳过了变量声明部分)。
Listing 3-27: Dynamic XML tags in explicit method calls
Dim book = _
New XElement(tagBook, _
New XAttribute(attrName, bookTitle), _
New XElement(tagAuthors, author1 & ", " & author2))
小贴士 我们并不是说在XML文本中你应该使用表达式来指定一个标签的名字。之所以强调上面这一点,目的是强调嵌套在XML文本中的表达式在生成XML架构时会立即被理解。一句话,我们可以不用内部的常量定义XML文本,但是我们认为这和普通方法调用会相比并不会显著的提高其可读性。
如果使用嵌入的表达式去定义XML的元素名称的话,我们必须注意闭合标签。在这种情况下,闭合标签中不能有标签的名。在Listing3-28中,闭合标签仅是</>,没有通过tagElement变量定义的标签名。
Listing 3-28: Closing tag for dynamic XML tags
1Dim tagElement = "Description"
2Dim sample = <<%= tagElement %>>Sample element</>
3
在XML文本中仅能嵌套一个表达式。因为XML文本是一个表达式,所以可以写如下的代码:
1Dim book = _
2 <Book Title="Introducing LINQ">
3 <%= <Publisher>Microsoft Press</Publisher> %>
4 </Book>
在这种情况下,Publisher元素就是一个嵌入到外部的Book元素的XML文本,是另一个XML文本。这个例子对于观察一个重要语法的详细内容有帮助。<%=标签必须和计算表达式在同一行,否则的话需要有一个续行符。%>标签和计算表达式的结尾必须在同一行上。换句话说,下面的代码是非法的:
1Dim book = _
2 <Book Title="Introducing LINQ">
3 <%=
4 <Publisher>Microsoft Press</Publisher>
5 %>
6 </Book>
但是我们可以用续行符来将代码写在不同的行上,如下面这个例子所示。注意续行符总是在XML文本外面:
1Dim book = _
2 <Book Title="Introducing LINQ">
3 <%= _
4 <Publisher>Microsoft Press</Publisher> _
5 %>
6 </Book>
如果想在一个“空缺”中用更多的表达式的话,我们必须定义一个元素的数组。例如,若要向一个“空缺”中放两个单独的Author元素的话,我们就可以像在Listing 3-29中的代码那样将XML文本用尖括号围绕起来。注意在数组初始化器中的第一个元素的末尾的续行符:
Listing 3-29: Nested list of XML literals
[译注]本段代码未通过测试。
<Book Title="Introducing LINQ">
<%= { <Author>Marco Russo</Author>, _
<Author>Paolo Pialorsi</Author> } %>
</Book>
我们刚才用一个数组把一个Author元素列表放进了一个Book元素中。因为,我们同样也可以将其他的生成IEnumberable对象的方法放进其他的XML元素中。
在Listing3-30是展示的声明中,我们使用了LINQ查询来从人的数组中获取作者列表。如你所见,XML文本可以嵌入到一个返回XML文本序列的查询表达式中, 该序列由引用查询返回的结果集其他嵌入表达式来生成。
Listing 3-30: Query embedded into an XML literal
[译注]原书代码无法验证通过
New {Name := "Paolo Pialorsi", Role := "Author"}, _
New {Name := "Roberto Brunetti", Role := "Reviewer"}}
Dim book = _
<Book Title="Introducing LINQ">
<%= From person In team _
Where person.Role = "Author" _
Select <Author><%= person.Name %></Author> %>
</Book>
[译注]修改之后的代码为:
2 New With {.Name = "Paolo Pialorsi", .Role = "Author"}, _
3 New With {.Name = "Roberto Brunetti", .Role = "Reviewer"}}
4 Dim book = _
5 <Book Title="Introducing LINQ">
6 <%= From person In team _
7 Where person.Role = "Author" _
8 Select <Author><%= person.Name %></Author> %>
9 </Book>
通过将LINQ语法和XML文本结合起来,我们可以以一种简单高效的方法来构建简单和复杂的包括查询结果的XML数据结构。
XML的延迟绑定(Late Binding over XML)
当我们想访问XML数据时,可能需要先导航到一个代表XML文档的层次结构的对象树。VB9提供了一些简化这种操作的语法,这种语法就是“对XML操作的延迟绑定”。
以一个movies的XML列表作为开始。每一个movie项必须有一个Title(属性)和一个Director(元素),再加上的genres列表。下面的代码展示了我们事例代码中的部分初始化代码:
1Dim movies = _
2<Movies>
3 <Movie Title="Fight Club">
4 <Genre>Crime</Genre>
5 <Genre>Drama</Genre>
6 <Genre>Thriller</Genre>
7 <Director>David Fincher</Director>
8 </Movie>
9 <!−other movies not shown here -->
10</Movies>
我们展示的第一个XML语法是child axis。如果我们写movie<Genre>的话,我们就得到了所有的选中的movie的genres。如Listing3-31所示:
Listing 3-31: Child axis
(From movie In movies.<Movie> _
Where movie.@Title = "Fight Club" _
Select movie).First()
' Get the first genre
Dim firstGenre = fightClub.<Genre>(0).Value
' Corresponds to: firstGenre = fightClub.Elements("Genre")(0).Value
Console.WriteLine("First = {0}", firstGenre)
' Display all genres
' Corresponds to: fightClub.Elements("Genre")
For Each g In fightClub.<Genre>
Console.WriteLine(g.Value)
Next
如果查询仅提供了一行,或者我们仅对查询结果的第一行有兴趣的话,那么就可以访问集合的第一个元素。例如,fightClub.<Genre>(0)允许访问一想相关的XElement实例。如果我们需要它的值或者属性,那么就需要读取它的Value属性。然而,这个语法可能会返回不止一行。在这种情况下,依次遍历所有的行就可以访问到它们了。
child axix语法被翻译为对Element的调用,将元素的名称指定为一个参数。该语法的详细信息请参阅Listing3-31的代码注释。通过attibute axis可以访问到属性。fightClub.@Title能够得到属性的值。如Listing 3-32所示:
Listing 3-32: Attribute axis
' Display the Title attribute of Fight Club movie
Console.WriteLine(fightClub.@Title)
' Corresponds to: Console.WriteLine(fightClub.Attribute("Title").Value)
属性轴被翻译为对Attribute的调用,将该属性名作为参数。上一个操作是是“子代轴(descendants axis)"。它允许你获取一个元素的所有子元素,无论子元素在层次结构中的位置如何。在Listing3-33中的代码和上面的代码相类似,但是在这里有三个点,而不是上面的一个:
Listing 3-33: Descendants axis
2Dim directors = movies<Director>
3For Each director In directors
4 Console.WriteLine(director)
5Next
6
在这种情况下,Listing3-33的输出结果展示了可能的复制品。使用Distinct操作符能够“清除”多余的输出。在第四章中会对此进一步进行讨论。
在第六章中我们将会对XML和.NET编译器的集成进行更深入的分析。
宽松代理(Relaxed Delegates)
在VB的8.0和更早期的版本中,如果我们想将一个方法绑定到代理上的话,这两个签名(方法和代理)必须完全一致。而在C#中方法仅需要一个能够“兼容”的签名。例如,在参数中的较少的特殊类型也是允许的。Visual Basic 9.0去除了以前的这些限制,现在我们可以写像Listing3-34中的代码:
Listing 3-34: Relaxed delegates
Public Class DemoEvent
Public Event Click As EventHandler
End Class
Module Application
Public WithEvents A As DemoEvent
Sub DemoOnClick(ByVal s As Object, ByVal e As Object) Handles A.Click
Console.WriteLine("Hello World")
End Sub
'
End Module
在Listing3-34中,对在VB8.0中可能产生错误的代码进行了高亮显示。在VB8中,若想编译通过,我们必须像下面的代码这样来写(已经对改变了的第二个参数进行了高亮显示):
1Sub DemoOnClick(ByVal s As Object, ByVal e As EventArgs) Handles A.Click
如果不在绑定到代理的方法内部使用参数的话,我们可以在方法声明中忽略整个参数列表。Handles关键字从相应的代理声明中推断出该方法的真正签名。在Listing3-35中,我们可以以另一种方式写前面的DemoOnClick方法。C#是不允许这种语法的,因为C#中没有和Handles相对应的关键字。
Listing 3-35: Relaxed delegates with signature inference
Console.WriteLine("Hello World from")
End Sub