Visual Basic 2010有哪些新的改进?

[原文作者]:Jonathan Aneja

[原文链接]:What’s New in Visual Basic 2010

Jonathan Aneja

    自1991年面世以来,Visual Basic一直都是创建项目的高效工具。大概20年后,它仍然提供调用微软.net框架的简单方法,支持开发者开发桌面、手机、浏览器甚至云程序。

    这个月微软将会发布Visual Studio 2010,它包含了Visual Basic的10版本(有时候被叫做VB2010或者VB10)。这个强大的版本包含了许多节约时间的功能,它能够帮助开发者以最少的代码做最多的事情。为了顺利的使用Visual Studio 2010里的Visual Basic,下面是所有你需要知道的。

共同改进(Coevolution)

    以前,Visual Basic和C#是由不同的组开发的,这就导致了一些功能在一种语言上比在另一种语言上先出现。比如,C#有自动属性和集合初始器,Visual Basic却没有;Visual Basic有后期绑定和可选参数这些功能,C#却没有。但是当一个新的功能出现在一种语言上时,许多客户会要求另一种语言也可以兼容这些功能。

    针对这些反馈,微软将Visual Basic和C#组合并,采用共同演化的策略,为的就是让各种语言能共同发展。当主要的功能在一种语言中被提出,它也应该出现在其他的语言当中。这并不意味着每一个功能都会在这两种语言中出现并且以同样的方式运作;事实上,每一种语言都有它自己的历史,意义和感觉——需要去维持的重要特征。共同演化意味着你能在一种语言里完成的功能在另一种语言里也同样简单实现。

    在.NET框架4里,Visual Basic和C#朝这个目标都迈出了巨大的一步,都增加了一些另一种语言已经具有的功能。然而,共同演化并不只是关于过去,它也是语言在未来的革新。从这个意义上来说,.NET框架4推出了强大的新功能,像动态语言运行时,嵌入操作类型和泛型差异,对这两种语言,Visual Basic和C#开发者可以充分利用.NET框架的优点。

Visual Basic 2010的新功能

    Visual Basic 2010的新功能是为了帮助你用更少的代码实现更多的功能。我们(Visual Basic设计组)看到开发者往往不得不写很多冗长的样板代码,调查之后我们决定让编译器去做这部分工作。这只是个蓝图,让我们一个个的深入这些功能。

隐行延续(Implicit Line Continuation)

    Visual Basic是一种面向行的语言,它使用清晰的,类似于英语的语法来提高可读性。但是,这往往导致在代码中碰到了每行80个字符的限制,迫使开发者使用鼠标滚动很多。你可以使用下划线来告诉编译器,它应把下一行作为当前行的一部分来处理(即将多个物理行作为一个单独的逻辑行来处理)。但是不得不重复的输入下划线也一直使人困扰,事实上,几年来,对编译器要求最多的功能就是解决这个问题。

然而,在Visual Basic 2010,编译器做到了。现在,哪些记号(像逗号,句号,运算符)会出现在续行字符之前,编译器都知道,并且会插入这些字符,所以开发者不用做这些了。例如,用一个逗号结束Visual Basic语句不再合法;编译器知道这些,所以当它发现一个记号看起来像逗号,回车,它就会推断续行符的存在,就像图1上的例子所示。

图1 推断续行符

<Extension()>

Function FilterByCountry(

  ByVal customers As IEnumerable(Of Customer),

  ByVal country As String) As IEnumerable(Of Customer)

    Dim query =

      From c In customers

      Where c.Country = country

      Select <Customer>

        <%=

          c.Name &

          "," &

          c.Country

        %>

      </Customer>

    Return query

  End Function

在Visual Basic 2008,图1的代码会需要9个下划线。然而,在每个这样的例子里,编译器推断在什么时候需要下划线并且允许它被省略:

· 在<Extension()>属性之后

· 在函数的声明里( (左括号)之后

· 在第一个参数,之后

· 在函数的声明里)(右括号)之前

· 在=(等号)之后

· 在<%=(嵌入表达式的开始标签)之后

· 在XML文档每个&(与符号)之后

· 在%>之前(嵌入表达式的结束标签)

如在例子中表现的一样,如果每个部分都在同一行就要超过80个字符,这个编译器新的功能,对这种方法签名尤其有用。在图2,你将会看到记号和续行符隐藏的地方的所有组合。

图2 续行符隐藏的地方

记号

之前

之后

, (逗号),  .  (点),   >  (属性),   (  {  (左括号),   <%=  (嵌入表示式的开始 (XML 字符))

 

X

),   },   ,   ]  (右括号), %> (嵌入表达式的结束)

X

 

所有的 LINQ 关键字:

Aggregate, Distinct, From, Group By, Group Join, Join, Let, Order By, Select, Skip, Skip While, Take, Take While, Where, In, Into, On, Ascending, Descending

X

X

运算符:

+ ,   – ,   * ,   / ,   \ ,   ^ ,   >> ,   << ,   Mod,   & ,   += ,   -= ,   *= ,   /= ,   \= ,   ^= ,   >>= ,   <<= ,   &= ,   < ,   <= ,   > ,   >= ,   <> ,   Is,  IsNot,  Like,  And,   Or,  Xor,  AndAlso,  OrElse

 

X

With (in an object initializer)

 

X

    
正如你看到的,有60多个地方不需要下划线。(事实上,在这片文章中,没有一个代码示例需要续行符。)当然,你仍然可以使用下划线,所以以前版本上的Visual Basic代码仍然如我们期望的兼容。

声明Lambdas表达式(Statement Lambdas)

Lambdas这个词初听起来会有点吓人,但Lambdas简单来说是定义在一个方法内的方法。Visual Basic 2008用函数关键字来介绍lambdas表达式:

Dim customers As Customer() = …

Array.FindAll(customers, Function(c) c.Country = "Canada")

Lambdas表达式提供一种相当不错的紧凑的方式去本地化表达逻辑,而不必分割成多个方法。例如,下面是之前的code在Visual Studio 2005(不支持 Lambdas表达式)里的情况:

Dim query = Array.FindAll(customers, AddressOf Filter)

    …

Function Filter(ByVal c As customer) As Boolean

  Return c.Country = "Canada"

End Function

不幸的是,Visual Studio 2008中的Lambdas表达式要求表达式返回一个值,所以:

Array.ForEach(customers, Function(c) Console.WriteLine(c.Country))

将会导致:

‘Compile error: "Expression does not produce a value."

Console.WriteLine是一个Sub过程(在C#中返回空),所以它没有返回值,这就是为什么编译器报一个错误。解决这个问题,Visual Studio 2010支持Statement Lambdas,就是可以包含一个或多个声明的Lambdas。

Array.ForEach(customers, Sub(c) Console.WriteLine(c.Country))

因为Console.WriteLine没有返回值,我们可以创建一个Sub lambdas,而不是一个Function lambdas。下面是另一个使用多个声明的例子:

Array.ForEach(customers, Sub(c)

                           Console.WriteLine("Country Name:")

                           Console.WriteLine(c.Country)

                         End Sub)

当执行这段代码,它会为每个customer打印2行,还要注意,当你在编码时悬停,你会注意到编译器已经推断出类型为Customer(键入 c As Customer来声明类型也是合法的)。动态布线事件处理是statment lambdas的另一大用途:

AddHandler b.Click, Sub(sender As Object, e As EventArgs)

                      MsgBox("Button Clicked")

                      ‘insert more complex logic here

                    End Sub

并且事实上,你可以将statement lambdas同Visual Studio 2008里提到的一个功能合并到一起:松弛委托。(你可以使用委托——类型安全的函数指针——来一次执行多个函数。)这种结合产生一种更简单的签名:

AddHandler b.Click, Sub()

                      MsgBox("Button Clicked")

                     ‘insert more complex logic here

                    End Sub

松弛委托让你从一个事件处理程序里完全省略参数——非常好的优点,这些参数频繁的提供但却从来没使用过,所以只会增加视觉噪音。

除了我们已经看到的单行Sub lambdas和多行Sub lambdas,Visual Studio 2010也支持多行Function lambdas:

Dim query = customers.Where(Function(c)

                              ‘Return only customers that have not been saved

                              ‘insert more complex logic here

                              Return c.ID = -1

                            End Function)

    另外有趣的是,Statement lambdas跟Visual Basic 2008的匿名委托采用相同的方式。人们经常把它跟C#的匿名方法混淆,但是技术上来说,他们并不一样。匿名委托适用于Visual Basic编译器推断委托类型是基于一个lambda的方法签名的时候。

Dim method = Function(product As String)

               If product = "Paper" Then

                 Return 4.5 ‘units in stock

               Else

                 Return 10 ’10 of everything else

               End If

             End Function

MsgBox(method("Paper"))

     如果你运行这段代码,你将会看到4.5显示在信息窗口上。当然,如果你悬停在这个方法上,你会看到Dim method As <Function(String) As Double>。因为我们不提供真正的委托类型,所以,编译器会自动生成一个,就像:

Delegate Function $compilerGeneratedName$(product As String) As Double

     这就叫匿名委托,因为它只出现在编译器生成的代码里,而不在自己写的代码里。事实上请注意,当没有As语句用来说明lambda的返回类型时,编译器推断返回值是Double型。编译器查看所有lambda里的返回语句,发现Doule类型(4.5)和Integer类型(10):

‘Notice the "As Single"

Dim method = Function(product As String) As Single

               If product = "Paper" Then

                 Return 4.5 ‘units in stock

               Else

                 Return 10 ’10 of everything else

               End If

             End Function

然后运行主导型算法并确定它可以安全的把10转换成Double类型,但是不能安全的将4.5转换成Integer类型,因此Double型是更好的选择。

当然如果编译器不尝试去推断类型,你也可以明确控制返回值的类型,而不是

VB XML手册5:”Halloween”问题

[原文作者]:Doug Rothaus

[原文链接]:VB XML Cookbook, Recipe 5: The “Halloween” Problem (Doug Rothaus)

    在前两次的手册中,我们讲了关于用ReplaceWith这个方法来进行身份转换.如果这个方法满足了你的需要,它会在你的代码中引发另外一个问题–"Halloween".让我们看看这个问题到底是怎样的,怎么样去解决.(详细的"Halloween"问题和解决方案可以参考这个文档).

    "Halloween"描述的场景是,你有一个以某种方式更新的数据集同时这个数据集又在被遍历的话,你会得到一个空引用的异常,更坏的是,你的代码可能会因为修改错误的数据而崩溃.例如前面两个手册用到的代码段:

Private Sub Recipe5(ByVal xmlPath As String)

    Dim xmlDoc = XDocument.Load(xmlPath)

 

    Dim info = xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo>

 

    ‘ Replace e-mail address tags with mailto links.

    For Each email In info…<act:eMail>

      TransformEmail(email)

    Next

  End Sub

 

  Private Sub TransformEmail(ByVal email As XElement)

    Dim emailHtml = <div class="Email">

                      <a href=<%= "mailto:" & email.<act:eMailAddress>.Value %>>

                        <%= email.<act:eMailAddress>.Value %>

                      </a>

                    </div>

 

     email.ReplaceWith(emailHtml)

  End Sub

 

    如果你运行这段代码,就会得到下面的异常:

 

      这个异常的出现是因为For… Each循环正在循环用<eMail>元素查询出来的结果。在第一次调用TransformEmail个方法的时候,<eMail>这个元素已经被HTML替换掉了,结果,这个查询引用的就是已经不存在的XML元素了。

    要怎么解决这个问题呢?有几个解决方案,第一,你可能你已经注意到了在前面的手册中,把这个查询的结果用ToList方法返回一个List。像下面的代码

    For Each email In info…<act:eMail>

    改为:

    For Each email In info…<act:eMail>.ToList()

    返回一个List,表明要处理的就不再是一个查询结果,而更新就会作用到查询上面,例如用HTML替换XML,这样就不会影响到List。

    其他比较普遍的解决方案就是你的代码不要修改原本的文件,把结果放到新的XML文件中。下一个手册我会讲怎么做。

VB XML手册1:用XML常量进行XML转换

[原文作者]Doug Rothaus
 
     我正在做一个关于用Visual Basic XML Literals取代XSLT转换的博客,我发现这个博客将会很长,所以,Avner Aharoni 和我谈了之后决定像几个月前我们做的LINQ操作指南一样把它分为多个条目并成一个系列。
 
     先介绍一下VB XML的操作指南。这个操作指南介绍了如何在Visual Basic中使用XML Literals的快速而简单的方法。在很多情况下,我们将会参考有着直接或者是相近功能的XSLTXPath你将会发现用XML Literals来转换和翻译XML是一件快速而简单的事情,XML的轴属性,和LINQXML,这一切都是从Visual Basic 2008开始的。
 
    首先介绍如何用XML LiteralsLINQ执行简单的XML转换。每个例子都用了一个嵌入式表达,从LINQ队列或者其他的资源,比如属性或函数中返回一组XML Literals,只需几行Visual Basic代码就能完成整个XSLT的转换。
 
     先看第一个例子,我们用了一个包含SQL ServerXML文件(AWContacts.xml)。每一个<Contact>标签都有一个<EmailAddress>的子标签,下面的代码创建了一个新的在代码中含有e-mail地址的XML文件。

Imports <xmlns="http://SampleSchema/AWContacts">
 
Public Class XMLCookbook
 Private Sub Recipe1()
   Dim xmlDoc = XDocument.Load("AWContacts.xml")
 
   Dim emailDoc = <?xml version="1.0"?>
                   <EmailAddresses>
                       <%= xmlDoc.<Contacts>.<Contact>.<EmailAddress> %>
                  </EmailAddresses>
 
   emailDoc.Save("EmailAddresses.xml")
 End Sub
End Class

 

     如果用XSLT进行转换,我们将要花费更多。在这里,你只是用了一个Visual Basic创建一个template函数,然后用XML Literals加载并保存XML,就像创建一个新的XML文件一样。XML Child Axisproperty引用了一组<EmailAddress>子标签,你也可以用embedded expressionXML文件中增加标签而不是<xsl:copy-of>,这样你就不用XSLTXpath转换这个文件。接下来我们看一个XSLTVisual Basic代码)转换的例子。

Recipe1.xslt
<?xmlversion=1.0?>
<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:aw="http://SampleSchema/AWContacts"
 xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo"
 xmlns:crm="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactRecord"
 xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
 <xsl:outputmethod="xml"indent="yes"/>
 <xsl:templatematch="aw:Contacts">
    <EmailAddresses>
      <xsl:copy-ofselect="aw:Contact/aw:EmailAddress"/>
    </EmailAddresses>
 </xsl:template>
</xsl:stylesheet>
Recipe1_XSLT Visual Basic Code
 
Public Class XMLCookbook
    Sub Recipe1_XSLT()
        Dim xslTransform As New System.Xml.Xsl.XslCompiledTransform
        xslTransform.Load("recipe1.xslt")
 
        Dim reader = Xml.XmlReader.Create("AWContacts.xml")
        Dim sw As New System.IO.StreamWriter("Recipe1.xml")
        Dim writer = Xml.XmlWriter.Create(sw)
 
        xslTransform.Transform(reader, writer)
        sw.Close()
    End Sub
End Class

 

     这里的XSLT转换出两端大量的代码,让我们花费了大量的工作创建VB版本。在Visual BasicXML命名空间比XSLT1.0)简单,我可以为XML元素定义一个缺省的XML命名空间而不需要特别的去定义一个XML命名空间。
     还有重要的一点,在Visual Basic能为XML Literals提供只能的支持,通过增加在博客附件中的schema文件然后继承这些schemas,我得到一个完整的XML子元素的列表,欲了解更多的信息,请参考XML Intellisense in Visual Basic.
 
     使用LINQ
 
     然后,你可以用LINQ在转换中增加一些查询功能。在XSLT中,你可以很轻易的用 <xsl:for-each>循环和 <xsl:if>判断来完成这个功能
 
     例如,在源文件中,每一个<Contact>标签都有一个<EmailPromotion>的子标签,这个标签确定了是否希望从该公司接收e-mail。下面的代码给出了一个例子,但是只包含了接受e-mail许可的e-mail地址。
 Dim promoList = <?xml version="1.0"?>

                  <EmailPromotionList>
                      <%= From contact In xmlDoc.<Contacts>.<Contact> _
                          Where contact.<EmailPromotion>.Value > 0 _
                          Select contact.<EmailAddress> %>
                  </EmailPromotionList>

     下面是相比较的XSLT代码:

 <xsl:templatematch="aw:Contacts">

    <EmailAddresses>
      <xsl:for-eachselect="aw:Contact">
        <xsl:iftest="aw:EmailPromotion &gt; 0">
          <xsl:copy-ofselect="aw:EmailAddress"/>
        </xsl:if>
      </xsl:for-each>
    </EmailAddresses>
 </xsl:template>

      上一个例子中,我们进一步的把源XML文件转化成一种新的格式。也可以用一个LINQ查询返回一组XMLLiterals。用嵌入式表达复制源文件中的值到新的XML Literal的元素或属性中。例如,新的文件将把<EmailAddress>替换为<Email>,原本<EmailPromotion>包含的信息转换为<Email>中的属性。

 
 Dim transformList = <?xml version="1.0"?>

                      <EmailPromotionList>
                          <%= From contact In xmlDoc.<Contacts>.<Contact> _
                              Where contact.<EmailPromotion>.Value > 0 _
                              Select <Email
                                       promotion=<%= contact.<EmailPromotion>.Value %>>
                                       <%= contact.<EmailAddress>.Value %>
                                     </Email> %>
                      </EmailPromotionList>

     下面是相比较的XSLT代码:

 <xsl:templatematch="aw:Contacts">

    <EmailPromotionList>
      <xsl:for-eachselect="aw:Contact">
        <xsl:iftest="aw:EmailPromotion &gt; 0">
          <Email>
            <xsl:attributename="promotion">
              <xsl:value-ofselect="aw:EmailPromotion"/>
            </xsl:attribute>
            <xsl:value-ofselect="aw:EmailAddress"/>
          </Email>
        </xsl:if>
      </xsl:for-each>
    </EmailPromotionList>
 </xsl:template>

      总之,我们看到如何使用Visual Basic, XML LiteralsLINQ来代替<xsl:copy-of>, <xsl:for-each>, <xsl:template>, <xsl:if>, <xsl:value-of> <xsl:attribute>标签,并用XML轴属性代替Xpath创造一个强大又简单的XML转换工具。

请看更多。。。。

Visual Basic 2008 隐藏彩蛋

[原文作者]:

[原文链接]:Hidden Gems in Visual Basic 2008

    昨天,我答应写一篇关于 Visual Basic  Visual Studio 2008里隐藏彩蛋的文章,这些彩蛋是你们在博客帖子或会议上从未见过的。我曾提及到了一些我最喜爱的特性,在BataⅠ版本的阐述中我尽情炫耀了智能感知(intellisence)的改进,尤其是对关键字、本地变量和表达式,自动完成语句的改进。在那篇文章中,我也提到一种语法新特性非严格委托(Relaxed Delegates),它允许你为事件(event)提供一个可替代的签名(alternate signatures)。这些都是很棒的特性但我要告诉你一些玩意!
    经过队员们的民意调查,最终我们挑选出了10个最重要的特性,我以前的文章里没有提及过的。我这里只是简单介绍,有兴趣的朋友可以继续研究。首先 ,我必须从基调特性(keynote feature)开始讲……
 
0)    多个 target Framework 支持(Multi-targetting

    简单的说,Multi-targetting支持你在VS2008VB9下用.NET 2.0 framework 进行编译。下面我将要讲到的所有特性都在.NET 2.0下有效,所以当你打开Visual Studio 创建一个project以后,把framework target设置为2.0(除了第5和第7项,因为它们需要LINQObjectsXML APILINQ是在.NET 3.5才推出)。

1)    输入推断(Type Inference
    Visual Basic 9里面,下面的这一小段代码以及Nothing都是延迟绑定(late-bound——在编译时(compile tine)一切都是延迟绑定,这就意味着你可以得到智能感知和输入推断。 
Dim dialog = New OpenFileDialog()       
Dim result = dialog.ShowDialog()       
Dim printStr = "C\"       
If result = Windows.Forms.DialogResult.OK Then           
printStr = dialog.FileName       
End If       
MsgBoxprintStr 
    这使得在输入代码时更加快捷,简单和准确。 这个特性和控制它的机制你可以在我们QA team里的Bill Horst写的文章里得到详尽描述。
2)    IF操作符
    还记得吗,IIF函数能返回一个对象(Object),这意味着你不需要通过智能感知或类型推断就可以得到默认的返回值(Object类型)。如果你坚持要类型安全或代码前绑定,可以强制转换,代码如下: 
 Dim intC As Integer = CIntIIfintA = intB intA intB – 1)) 
    现在用IF操作符,你可以这么写: 
Dim intD As Integer = IfintA = intB intA intB 
     如果加上类型推断,代码就更简单了: 
 

    我本人是能提高易读性的特性的坚定拥护者。
3)    对象初始化(Object Initializers
     总的来说,在.net framework里,对象初始化是一种类似于把Dimstatement combined整合在一起的表达式。这样使得参数构造器多少让人容易接受一些: 
Dim strm As New StreamWriter _                 
New FileStream"C\out.txt"FileMode.OpenOrCreate _                        
With {.Position = 10} 
    对象初始化使得创建一个数组对象更加容易: 
Dim Capitals() As City = {New City With {.Name = "Antanarivo" .Country = "Madagascar"} _
New City With {.Name = "Belmopan" .Country = "Belize"} _
New City With {.Name = "Monaco" .Country = "Monaco"} _New City With {.Country = "Palau" .Name = "Koror"}}
4)    允许空值(Nullable
    Nullable 是一种特性,可能你知道但没有关注过。它是一个基本的.NET 表达式,专门针对nullable 类型(整数,日期,等等)。在 LINQ  SQL 中使用 Visual Studio 2008中引入的对象相关映射层这个设计器,数据库中的可空行都被映射成为这种类型。其结果是你可以在VB中写出像下面这样的表达式,而得到正确的结果其中的空属性以null值传播。例如下面这点代码,在Country type中有个independence property nullable date 
Dim virginIslands As New Country With {.Independence = Nothing}       
Dim palau As New Country With {.Independence = #10/1/1994#}        
Dim vILength = #8/24/2005# – virginIslands.Independence ‘ Nothing       
Dim pLength = #8/24/2005# – palau.Independence ‘ 3980.000000
5)    LINQ to DataSet
     我爱死这个特性了,因为它意味着你可以不用调用其他数据访问技术就能收获LINQ的好处。我先填充了一个 DataSet,然后就可以对这个DataSet进行查询。 
Me.EmployeesTableAdapter.FillMe.NORTHWNDDataSet.Employees       
Dim query = From emp In Me.NORTHWNDDataSet.Employees _                   
Where emp.Country = "USA" _                   
Order By emp.HireDate _                   
Select emp       
Me.EmployeesBindingSource.DataSource = query 
6)    语法提示(Syntax Tooltips 
 
     

    再看看这个 

    还有这个

 

7)    XMLnamespace支持智能感知
    之前我们已经在博客上讲到过XML Intelligence,但是我们忽视了一点。
namespace被用在XML 文档中时,智能感知会对namespace前缀和local name进行匹配,你只要为输入带来很大便捷,你只要输入开头几个字母然后回车,VS会帮你找到匹配的字段并加上相应的前后缀。下面是个小例子,以一个输入文件开始,然后使用智能感知。
 

    此时我们只输入字母tVS会自动选中tomato 
 
 

8)    转到类型定义(GoTo Type Definition
    通常,当你定义了一个变量,你想通过Object Browser浏览它在代码中的类型定义的时候,现在你多了一种选择,通过context menu可以让你直接找到它的定义。这点非常好,尤其是涉及到类型推断时能帮你确定该变量的类型是否和你想象的一致。 
 
 

9)  循环变量的输入推断(Type inference for loop variables
    检查下面这段代码: 
 
    还有这段:
 
 

     如果没有指定控制变量的类型,它会根据表达式或循环的信息从右往左推断。

10)    性能的提高以及非封闭性操作 
    后台编译器有一个非常强大的特性,它可以给你及时地反馈只要你写的代码正确。在这个版本的Visual Studio中我们对后台编译器做了很大的改进,我们相信后台编译器比以前快3倍但只用原先1/3的内存。任何使用过VS2008 的人都会意识到这一点。虽然我们在性能上做了很大的改进,但是在大项目里面某些操作符仍然是一个巨大的花费,例如改变一个base classdeclaration 通常会被多次用到。如果在后台编译器工作之前,你试图调用一些依赖于编译信息的特性(例如IntellisenceDrop Downs),在以前版本的VS中会有一个长时间的停顿直到编译完成为止,但是现在这个问题解决了,当你想得到drop downs的时候就像这样: 
 
    集成开发环境照样能够响应用户操作。 

VB XML 手册2:子代和父代

[原文作者]Doug Rothaus
 
 
   这个手册主要是讲,在XML文件中怎样使用Visual BasicXML轴属性和LINQ toXML对象访问子和父元素。
 
 
   Visual Basic提供XML轴属性,可以很容易的引用子节点和标记。这是使用XML经常会遇到的情况。你可能会需要引用下一级元素在XML的数据层次上的不同层级上出现。那样的话,你可以使用XML 子代轴属性
 
   XML子代轴属性是用三个跟随着你想引用的XML元素的周期来定义的。例如,在手册1中用到的AdventureWorks contacts source文件中(你可以从手册1下载这个XML文件和相关的模式)。里面有个<AdditonalContactInfo>元素,包括了关于联系方式例如电话号码,发货和帐单地址等等的信息。在<AdditionalContactInfo>元素里面的电话号码可以出现在这个元素值里面的任何位置。最终,你就可以用XML子代轴属性来引用电话号码这个元素。例如:
 
Dim xmlDoc = XDocument.Load("AWContacts.xml")
 
‘ Return all descendant <telephoneNumber> elements
Dim telephoneNumbers = _
 xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo><act:telephoneNumber>
 
   XML子代轴属性返回XML数据层次中所有符合元素的集合。我们看一个更加完整的例子。下面的方法用XML子代轴属性返回一串联系名字和电话号码。
 
Imports <xmlns="http://SampleSchema/AWContacts">
Imports <xmlns:s="sample-output">
Imports <xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo">
Imports <xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
 
Public Class Recipe2
 
 Public Function GetPhoneNumbers(ByVal xmlDoc As XDocument) As XElement
    Return <s:ContactPhoneNumbers>
             <%= From contact In xmlDoc.<Contacts>.<Contact> _
                 Where contact.<aci:AdditionalContactInfo><act:telephoneNumber>.<act:number>.Count > 0 _
                 Select <s:Contact>
                          <s:Name><%= contact.<FirstName>.Value & " " & _
                                      contact.<LastName>.Value %></s:Name>
                          <s:PhoneNumbers>
                            <%= From number In _
                                contact.<aci:AdditionalContactInfo><act:telephoneNumber>.<act:number> _
                                Select <s:Number><%= number.Value %></s:Number> _
                            %>
                          </s:PhoneNumbers>
                        </s:Contact> _
             %>
           </s:ContactPhoneNumbers>
 End Function
End Class
 
XSLT中,同样传输是这样的:
 
<?xmlversion=1.0?>
<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:aw="http://SampleSchema/AWContacts"
 xmlns:s="sample-output"
 xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo"
 xmlns:crm="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactRecord"
 xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
<xsl:outputmethod="xml"indent="yes" />
 
 <xsl:templatematch="aw:Contacts">
    <s:ContactPhoneNumbers>
      <xsl:apply-templatesselect="aw:Contact" />
    </s:ContactPhoneNumbers>
 </xsl:template>
 
 <xsl:templatematch="aw:Contact">
    <xsl:iftest="count(aci:AdditionalContactInfo//act:telephoneNumber/act:number) > 0">
      <s:Contact>
        <s:Name>
          <xsl:value-ofselect="aw:FirstName"/>
          <xsl:text> </xsl:text>
          <xsl:value-ofselect="aw:LastName"/>
        </s:Name>
        <xsl:apply-templatesselect="aci:AdditionalContactInfo" />
      </s:Contact>
    </xsl:if>
 </xsl:template>
 
 <xsl:templatematch="aci:AdditionalContactInfo">
    <s:PhoneNumbers>
      <xsl:for-eachselect=".//act:telephoneNumber/act:number">
        <s:Number>
          <xsl:value-ofselect="."/>
        </s:Number>
      </xsl:for-each>
    </s:PhoneNumbers>
 </xsl:template>
 
</xsl:stylesheet>
 
   一个比较重要的需要注意的是,当子元素的位置总是一样的时候就不应该用XML子代轴属性。如果用XML 子轴属性去引用XML数据层次中的特定位置,你的代码会比使用XML子代轴属性去搜索整个XML数据层次中符合的项执行起来好很多。
 
父代
 
   XML轴属性使向下搜索XML数据层次快并且简单。然而,如果你想向上搜索XML数据层次呢?那样的话,你可以用XNode类的Ancestors方法。因为XML直接把LINQ to XML类的功能暴露出来,你可以用那些LINQ to XML类附带的功能。
 
   Ancestors()这个方法在XML数据层次中向上搜索那些符合给出元素名字的元素。传给Ancestors方法的元素的名字必须包括元素XML命名空间。可以用GetXmlNamespace operator返回基于给出的XMLprefix的命名空间。对于默认的XML命名空间,调用GetXmlNamespace操作不传入prefix。如果你在同样的XML命名空间中搜索一个已经引用的元素,可以用Name.Namespace属性访问已经引用的元素的命名空间。
 
   例如,下面的例子从AdventureWorks Contacts得到一串<telephoneNumber>元素(实际上,任何一个类型的phoneNumberType定义在ContactType.xsd文件里面),然后返回一串电话号码和跟电话号码相关的名字。这个例子调用Ancestors()方法去向上搜索XML数据层次中的<Contact>节点。
 
Imports <xmlns="http://SampleSchema/AWContacts">
Imports <xmlns:s="sample-output">
Imports <xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo">
Imports <xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
 
Public Class Recipe2
 
 Public Function GetPhoneList(ByVal numberList As IEnumerable(Of XElement)) As XElement
    Return <s:PhoneNumberList>
             <%= From number In numberList _
                 Let contact = number.Ancestors(GetXmlNamespace() + "Contact") _
                 Select <s:PhoneNumber>
                          <s:Number type=<%= number.Name.LocalName %>>
                            <%= number.<act:number>.Value %>
                          </s:Number>
                          <s:Owner><%= contact.<FirstName>.Value & " " & _
                                       contact.<LastName>.Value %></s:Owner>
                      </s:PhoneNumber> _
             %>
           </s:PhoneNumberList>
 End Function
End Class
 
XSLT中,同样的传输是这样的:
 
<?xmlversion=1.0?>
<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:aw="http://SampleSchema/AWContacts"
 xmlns:s="sample-output"
 xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo"
 xmlns:crm="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactRecord"
 xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
<xsl:outputmethod="xml"indent="yes" />
 
 <xsl:templatematch="aw:Contacts">
    <s:PhoneNumberList>
      <xsl:apply-templatesselect="aw:Contact//act:telephoneNumber" />
    </s:PhoneNumberList>
 </xsl:template>
 
 <xsl:templatematch="aw:Contact//act:telephoneNumber">
    <s:PhoneNumber>
      <s:Number>
        <xsl:attributename="type">
          <xsl:value-ofselect="name()" />
        </xsl:attribute>
        <xsl:value-ofselect="./act:number"/>
      </s:Number>
      <s:Owner>
        <xsl:value-ofselect="ancestor::aw:Contact/aw:FirstName"/>
        <xsl:text> </xsl:text>
        <xsl:value-ofselect="ancestor::aw:Contact/aw:LastName"/>
      </s:Owner>
    </s:PhoneNumber>
 </xsl:template>
</xsl:stylesheet>