自 1991 年 Visual Basic 语言诞生之日起,它就一直是生成应用程序的高效率工具。将近 20 年之后,它继续提供与 Microsoft .NET Framework 的轻松对接,使开发人员能够编写可跨越桌面、电话、浏览器甚至云的应用程序。
Microsoft 将在本月发布 Visual Studio 2010,其中包含 Visual Basic 版本 10(有时称为 VB 2010 或 VB10)。此版本是迄今为止最强大的版本,包含许多省时省力的功能,可以帮助开发人员通过更少的代码行完成更多的操作。在这里,将会为您提供所有必要的内容,让您充分了解并利用 Visual Studio 2010 中的 Visual Basic。
共同演变
在过去,Visual Basic 和 C# 是由独立团队分别开发的,这通常会导致一些功能先出现在一种语言中,继而又出现在另一种语言中。例如,C# 有 Visual Basic 中所没有的自动实现属性和集合初始值设定项,而 Visual Basic 则有 C# 中所没有的晚期绑定和可选参数等功能。但每当一种语言具有新功能时,许多客户都会要求将该功能也添加到另一种语言中。
为了解决这一需求,Microsoft 合并了 Visual Basic 和 C# 团队,实行共同演变的策略。目的是为推动这些语言共同发展。当一种语言中引入重大功能时,它也会出现在另一种语言中。这并不是说每种功能都将出现在两种语言中,并按完全相同的方式工作;实际上,每种语言都有自己的历史、灵魂和感觉 – 保留这些特性非常重要。
在 .NET Framework 4 中,Visual Basic 和 C# 朝这一目标迈进了一大步,分别吸收了对方既有的许多功能。然而,共同演变不仅影响到以前的功能;它同样是这些语言未来的发展策略。本着这种精神,.NET Framework 4 在两种语言中同时引入了强大的新功能,例如动态语言运行时、嵌入式互操作类型和泛型方差,从而使 Visual Basic 和 C# 开发人员能够充分利用 .NET Framework。
Visual Basic 2010 新增功能
Visual Basic 2010 中的新功能旨在帮助您通过更少的代码行实现更多操作。我们 Visual Basic 设计团队仔细研究了开发人员通常不得不编写大量繁琐样板代码的地方,并找到相应解决办法,让编译器代替执行此类工作。当然,这是从整体上来看,现在就让我们深入了解各项功能。
隐式行继续符
Visual Basic 是一种面向行的语言,它使用类似于英语的清晰语法来增强可读性。但这通常会导致代码遇到每行 80 个字符的限制,从而迫使开发人员要进行大量滚动。您可以使用下划线字符来告知编译器应将下一行作为当前行继续处理(也就是说,将多个物理行视为单个逻辑行)。但不得不重复地键入下划线字符一直很令人烦恼,而事实上多年以来排在首位的功能请求就是让编译器“解决这个问题”。
而在 Visual Basic 2010 中,编译器能够解决这个问题。编译器现在知道哪些标记(例如逗号、圆括号和运算符)往往出现在行继续符前面,并且它会插入字符,因此开发人员不再需要插入字符。例如,用逗号作为 Visual Basic 语句的结尾肯定不合逻辑;编译器知道这一点,因此,当编译器看到诸如 {comma, enter} 这样的标记流时,它会推断出存在行继续符,如图 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、&、+=、-=、*=、/=、/=、^=、>>=、<& lt;=、&=、<、<=、>、>=、<>、Is、IsNot、Like、And、Or、Xor、 AndAlso、OrElse |
X | |
With(在对象初始值设定项中) | X |
如您所见,有 60 多处该语言不需要下划线字符的地方。(事实上,本文中的任何一个代码示例都不需要行继续符。)当然,您仍然可以使用下划线字符,因此 Visual Basic 以前版本中的代码将仍然按预期方式编译。
语句 Lambda
术语 lambda 乍听上去可能很吓人,但 lambda 只是在另一个函数内定义的函数。Visual Basic 2008 引入了带 Function 关键字的 lambda 表达式:
Dim customers As Customer() = ...
Array.FindAll(customers, Function(c) c.Country = "Canada")
Lambda 表达式使您能够在本地以细致紧凑的方式表达逻辑,而不必跨多个方法拆分逻辑。例如,下面是 Visual Basic 2005(不支持 lambda 表达式)中以前的代码的表示形式:
Dim query = Array.FindAll(customers, AddressOf Filter)
...
Function Filter(ByVal c As customer) As Boolean
Return c.Country = "Canada"
End Function
不幸的是,Visual Basic 2008 的 lambda 表达式要求表达式返回值,因此以下代码:
Array.ForEach(customers, Function(c) Console.WriteLine(c.Country))
将会导致以下情况:
'Compile error: "Expression does not produce a value."
Console.WriteLine 是一个 Sub 过程(C# 中为 void),因此它不会返回值,而这就是编译器产生错误的原因所在。为了处理此情况,Visual Basic 2010 引入了对语句 lambda 的支持,后者是包含一个或多个语句的 lambda:
Array.ForEach(customers, Sub(c) Console.WriteLine(c.Country))
由于 Console.WriteLine 不返回值,因此我们可以只创建 Sub lambda,而不是 Function lambda。下面是使用多个语句的另一个示例:
Array.ForEach(customers, Sub(c)
Console.WriteLine("Country Name:")
Console.WriteLine(c.Country)
End Sub)
当此代码运行时,它将为每个客户打印两行。另外请注意,如果在编码时悬停在 c 上,您将看到编译器会将类型推断为 Customer(键入 c As Customer 来显式声明类型也是合法的)。动态编写事件处理程序是语句 lambda 的另一个出色用途:
AddHandler b.Click, Sub(sender As Object, e As EventArgs)
MsgBox("Button Clicked")
'insert more complex logic here
End Sub
并且,事实上,您可以将语句 lambda 与 Visual Basic 2008 中引入的一项功能(松散委托)结合使用。(可以使用委托 – 类型安全的函数指针 – 一次性执行多个函数。)这种组合将生成更为简单的签名:
AddHandler b.Click, Sub()
MsgBox("Button Clicked")
'insert more complex logic here
End Sub
委托松散使您可以完全忽略事件处理程序中的参数 – 这是一个很好的优点,只要它们根本未使用过,因此它们只会在视觉上带来干扰。
除了到目前为止我们已看到的单行 Sub lambda 和多行 Sub lambda 外,Visual Basic 2010 还支持多行 Function lambda:
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)
语句 lambda 的另一个引人关注的方面是它们与 Visual Basic 2008 引入的匿名委托的相交方式。人们经常将这些委托与 C# 的匿名方法混淆,尽管严ge来说它们并不相同。当 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。此外,如果悬停在 method 上,您将看到文本 Dim method As <Function(String) As Double>。由于我们未提供实际委托类型,因此编译器将自动生成一个委托类型,如下所示:
Delegate Function $compilerGeneratedName$(product As String) As Double
这称为匿名委托,因为它只会出现在编译器生成的代码中,而不会出现在编写的代码中。请注意,当事实上没有提供 As 子句来指定 lambda 的返回类型时,编译器将返回类型推断为 Double。编译器将查看 lambda 内的所有返回语句,并将确定类型 Double (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 是更好的选择。
您也可以显式控制返回类型,在这种情况下,编译器将不会尝试推断类型。非常常见的做法是将 lambda 赋给具有显式委托类型的变量,而不是依赖于编译器来推断委托类型:
Dim method As Func(Of String, Single) =
Function(product)
If product = "Paper" Then
Return 4.5 'units in stock
Else
Return 10 '10 of everything else
End If
End Function
由于提供了显式目标类型,因此无需声明 As String 或 As Single;编译器可基于语句左边的委托类型来推断出其存在。因此,如果您悬停在 product 上,将会发现推断出的类型为 String。不再必须指定 As Single,因为委托类型已提供该信息。在前面的示例中,Func 委托(.NET Framework 包括该委托)的签名如下所示:
Delegate Function Func(Of T, R)(ByVal param As T) As R
但有一个很小的例外之处,稍后我们将在“泛型方差”一节中看到。
自动实现的属性
在 Visual Basic 中,属性是用于向外部公开对象状态的类成员。典型的属性声明与如下声明类似:
Private _Country As String
Property Country As String
Get
Return _Country
End Get
Set(ByVal value As String)
_Country = value
End Set
End Property
一个实际上非常简单的概念就有 10 行代码。由于典型的对象通常有数十个属性,因此您最终会在类定义中包括大量样板代码。为了简化此类任务,Visual Basic 2010 引入了自动实现的属性,利用该属性,您只需使用一行代码即可定义简单的属性:
Property Country As String
在这种情况下,编译器将继续运行并自动生成 Getter、Setter 和支持字段。支持字段的名称是始终为前面带有下划线字符的属性的名称:此例中为 _Country。这种命名约定在将自动实现的属性更改为常规属性的情况下确保了二进制序列化兼容性。只要支持字段的名称相同,二进制序列化就将继续工作。
您可使用自动实现的属性执行的其中一项出色的功能是:指定在构造函数运行时设置属性默认值的初始值设定项。举例来说,一个带有实体类的常见方案将主键设置为类似于 -1 的值,以指示其处于未保存的状态。代码将如下所示:
Property ID As Integer = -1
当构造函数运行时,支持字段 (_ID) 将自动设置为值 -1。初始值设定项语法也适用于引用类型:
Property OrderList As List(Of Order) = New List(Of Order)
由于无需输入两次类型的名称,因此上一行代码可能不会具有非常明显的“Visual Basic 特征”。好消息是,常规变量声明中有一个与 Visual Basic 所允许语法一致的更短的语法:
Property OrderList As New List(Of Order)
您甚至能够将此语法与对象初始值设定项结合使用,以允许设置其他属性:
Property OrderList As New List(Of Order) With {.Capacity = 100}
很显然,对于更复杂的属性,扩展的语法仍然是必要的。您仍然可以键入 Property{Tab} 来激活旧属性片段。或者,在键入属性的第一行后,您可以只输入 Get{Enter},IDE 将生成旧样式的属性:
Property Name As String
Get
End Get
Set(ByVal value As String)
End Set
End Property
人们通常会发现:新的属性语法与公共字段的语法几乎相同,那么为什么不改为使用公共字段?有几个原因:
大多数 .NET 数据绑定基础结构都依据属性(而不是字段)工作。
接口无法强制要求存在字段;但可以强制要求存在属性。
属性为更改业务规则提供了更长期的灵活性。例如,假定某人引入了电话号码必须为 10 位数的规则。如果分配给公共字段,将无法执行此验证。对于诸如二进制序列化和反射等方案而言,将公共字段更改为属性是一项重大更改。
集合初始值设定项
一种常见 .NET 做法是实例化集合,然后通过为每个元素调用一次 Add 方法来填充该集合:
Dim digits As New List(Of Integer)
digits.Add(0)
digits.Add(1)
digits.Add(2)
digits.Add(3)
digits.Add(4)
digits.Add(5)
digits.Add(6)
digits.Add(7)
digits.Add(8)
digits.Add(9)
但对于从根本上而言非常简单的概念来说,将会产生大量语法开销。Visual Basic 2010 引入了集合初始值设定项,使您能够更轻松地实例化集合。对于此代码:
Dim digits = New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
编译器将自动生成对 Add 方法的所有调用。您也可以使用 Visual Basic 的 As New 语法的功能:
Dim digits As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
请注意,在 Visual Basic Team 上,我们一直建议使用第二种语法 (As New),而不是前者,因为它使代码更能适应 Option Infer 设置的更改。
您可以依据满足以下要求的任何类型使用集合初始值设定项:
您可以使用 For Each 语句循环访问该类型 – 也就是说,该类型实现 IEnumerable。(有关集合类型更精确/详细的定义,请参见 msdn.microsoft.com/library/aa711986(VS.71).aspx 上 Visual Basic 语言规范的第 10.9.3 节)。
该类型具有可访问的(但不一定是公共)无参数构造函数。
该类型具有可访问的(但不一定是公共)实例或名为 Add 的扩展方法。
这意味着,您也可以将集合初始值设定项用于更复杂的类型,例如字典:
Dim lookupTable As New Dictionary(Of Integer, String) From
{{1, "One"},
{2, "Two"},
{3, "Three"},
{4, "Four"}}
(请注意,即使此语句跨了五行,也没有下划线字符。)在这种情况下,编译器将生成与初始化字典的旧方法等效的代码:
Dim lookupTable As New Dictionary(Of Integer, String)
lookupTable.Add(1, "One")
lookupTable.Add(2, "Two")
lookupTable.Add(3, "Three")
lookupTable.Add(4, "Four")
编译器在调用具有两个 参数(而不是一个参数)的 Add 方法。它之所以知道这样做,原因是传入集合初始值设定项的值位于嵌套的大括号中,如下所示:{{1, “One”}, {2, “Two”}, …}。对于每一组嵌套的大括号,编译器会尝试将这些参数传递到兼容的 Add 方法。
也可以通过使用扩展方法来提供您自己的自定义 Add 实现:
<Extension()>
Sub Add(ByVal source As IList(Of Customer),
ByVal id As Integer,
ByVal name As String,
ByVal city As String)
source.Add(New Customer With
{
.ID = id,
.Name = name,
.City = city
})
End Sub
(看看所有这些缺失的下划线字符!)此方法扩展任何实现 IList(Of Customer) 的类型,然后允许您使用新的集合初始值设定项语法,如下所示:
Dim list = New List(Of Customer) From
{
{1, "Jon", "Redmond"},
{2, "Bob", "Seattle"},
{3, "Sally", "Toronto"}
}
(向列表 中添加三个客户)。您也可以将集合初始值设定项与自动实现的属性结合使用:
Property States As New List(Of String) From {"AL", "AK", "AR", "AZ", ...}
数组文本
除了更强大的集合类型处理方式外,Visual Basic 2010 还提供了一些用于处理数组的强大增强功能。假设有以下代码(在较旧版本中可正常工作):
Dim numbers As Integer() = New Integer() {1, 2, 3, 4, 5}
通过查看该数组中的元素,很明显每个元素都是整数,因此,必须实际上在此行中打印输出两次 Integer 的操作不会真正添加任何值。数组文本 允许将某个数组的所有元素放在大括号内,然后让编译器自动推断类型,从而创建该数组:
Dim numbers = {1, 2, 3, 4, 5}
numbers 的类型不是 Object,而是 Integer()(只要启用了“Option Infer”),原因是数组文本现在可代表本身,并且有其自己的类型。假设有一个更复杂的示例:
Dim numbers = {1, 2, 3, 4, 5.555}
在这种情况下,numbers 的类型将被推断为 Double()。编译器通过检查数组中的每个元素并计算基准类型(所使用的算法与前面讨论的用于推断语句 lambda 的返回类型的算法相同),从而确定类型。如果没有基准类型将会发生什么情况?例如,以下代码中所示:
Dim numbers = {1, 2, 3, 4, "5"}
在这种情况下,将 Integer 转换为 String 将会缩小转换范围(也就是说,在运行时可能会出现数据丢失的情况),同样,将 String 转换为 Integer 也会缩小转换范围。可选择的唯一安全的类型为 Object()(如果启用了“Option Strict”,编译器将产生错误)。
可以嵌套数组文本以形成多维数组或交错数组:
'2-dimensional array
Dim matrix = {{1, 0}, {0, 1}}
'jagged array - the parentheses force evaluation of the inner array first
Dim jagged = { ({1, 0}), ({0, 1}) }
动态语言运行时
尽管 Visual Basic 从技术上而言实质上是静态语言,但它一直有非常强大的动态功能,例如晚期绑定。Visual Studio 2010 附带了一个名为动态语言运行时 (DLR) 的新平台,利用该平台可更为轻松地生成动态语言 – 并在这些语言之间通信。Visual Basic 2010 已更新为在其晚期绑定程序中完全支持 DLR,从而使开发人员能够使用采用其他语言(例如 IronPython/IronRuby)开发的库和框架。
此功能的一项突出优点是,从语法上而言没有任何内容发生更改(事实上,在编译器中没有修改任何一行代码来支持此功能)。开发人员仍然能够像在 Visual Basic 以前的版本中一样进行晚期绑定的操作。发生变化的是 Visual Basic 运行库 (Microsoft.VisualBasic.dll) 中的代码,该运行库现在可识别 DLR 提供的 IDynamicMetaObjectProvider 接口。如果某个对象实现此接口,则 Visual Basic 运行库将构建 DLR CallSite,并允许该对象及提供该对象的语言将它们自己的语义注入操作。
例如,Python 标准库包含一个名为 random.py 的文件,其中有一个名为 shuffle 的方法,该方法可用于随机重新排列数组中的元素。调用该方法很简单:
Dim python As ScriptRuntime = Python.CreateRuntime()
Dim random As Object = python.UseFile("random.py")
Dim items = {1, 2, 3, 4, 5, 6, 7}
random.shuffle(items)
在运行时,Visual Basic 会看到对象实现 IDynamicMetaObjectProvider,并因此将控制权交给 DLR,后者随后将与 Python 通信,并执行该方法(将 Visual Basic 中定义的数组作为参数传递给该方法)。
这是调用启用了 DLR 的 API 的一个示例,但开发人员也可以创建他们自己的使用此功能的 API。关键是实现 IDynamicMetaObjectProvider 接口,在这种情况下,Visual Basic 和 C# 编译器将可识别具有特殊动态语义的对象。请不要手动实现该接口,更简单的方法是:从 System.Dynamic.DynamicObject 类(该类已实现此接口)继承并仅仅重写少数几个方法。图 3 显示了创建自定义动态对象(一种似乎可实时创建属性的“属性包”)并使用正常 Visual Basic 晚期绑定来调用该对象的完整示例。(有关使用 DynamicObject 的详细信息,请阅读 Doug Rothaus 撰写的非常不错的文章,网址为 blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx。)
图 3 创建自定义动态对象并使用 Visual Basic 晚期绑定调用该对象
Imports System.Dynamic
Module Module1
Sub Main()
Dim p As Object = New PropertyBag
p.One = 1
p.Two = 2
p.Three = 3
Console.WriteLine(p.One)
Console.WriteLine(p.Two)
Console.WriteLine(p.Three)
End Sub
Class PropertyBag : Inherits DynamicObject
Private values As New Dictionary(Of String, Integer)
Public Overrides Function TrySetMember(
ByVal binder As SetMemberBinder,
ByVal value As Object) As Boolean
values(binder.Name) = value
Return True
End Function
Public Overrides Function TryGetMember(
ByVal binder As GetMemberBinder,
ByRef result As Object) As Boolean
Return values.TryGetValue(binder.Name, result)
End Function
End Class
End Module
泛型方差
这是一项乍听起来的确可能很复杂的功能(带有像协方差和逆变这样的术语),但实际上它很简单。如果您有类型为 IEnumerable(Of Apple) 的对象,并且希望将其分配给 IEnumerable(Of Fruit),这应是合法的,因为每个 Apple 都是 Fruit(由继承关系强制要求)。遗憾的是,在 Visual Basic 2010 之前,编译器中不支持泛型方差,即使公共语言运行时 (CLR) 中实际上支持泛型方差也是如此。
让我们看一下图 4 中的示例。在 Visual Basic 2008 中,图 4 中的代码将在 Dim enabledOnly 行上产生编译错误(或者,如果禁用了“Option Strict”,则产生运行时异常)。解决方法是调用 .Cast 扩展方法,如下所示:
'Old way, the call to Cast(Of Control) is no longer necessary in VB 2010
Dim enabledOnly = FilterEnabledOnly(buttons.Cast(Of Control))
这一点不再必要,因为在 Visual Basic 2010 中,已通过使用 Out 修饰符将 IEnumerable 接口标记为协变:
Interface IEnumerable(Of Out T)
...
End Interface
图 4 泛型方差示例
Option Strict On
Public Class Form1
Sub Form1_Load() Handles MyBase.Load
Dim buttons As New List(Of Button) From
{
New Button With
{
.Name = "btnOk",
.Enabled = True
},
New Button With
{
.Name = "btnCancel",
.Enabled = False
}
}
Dim enabledOnly = FilterEnabledOnly(buttons)
End Sub
Function FilterEnabledOnly(
ByVal controls As IEnumerable(Of Control)
) As IEnumerable(Of Control)
Return From c In controls
Where c.Enabled = True
End Function
End Class
这意味着泛型参数 T 现在为变量(也就是说,它适用于继承关系),并且编译器将确保仅在类型来自于接口的位置使用该参数。泛型参数也可以是逆变量,这意味着它们仅在输入 位置使用。类型可实际上具有这两者。例如,前面讨论的 Func 委托既具有逆变参数(传入的内容),也具有协变参数(用于返回类型):
Delegate Function Func(Of In T, Out R)(ByVal param As T) As R
可以在自定义接口和委托上使用 In 和 Out 修饰符。.NET Framework 4 中的许多常用接口和委托已标记为变量;常见示例包括所有 Action/Func 委托、IEnumerable(Of T)、IComparer(Of T) 和 IQueryable(Of T) 等。
泛型方差的突出优点是:它是一项您完全无需担心的功能 – 如果它在执行工作,您将绝不会注意到它。曾经会导致编译器错误或要求调用 .Cast(Of T) 的情形在 Visual Basic 2010 中工作正常。
改进的可选参数
可选参数提供了一种有用的高效功能,它使开发人员能够建立更灵活的方法,并避免使用许多方法重载使类混乱不堪。在过去有一点限制,即可选参数不能为 null(或者甚至不能为任何非内部结构类型)。Visual Basic 2010 现在允许您定义任意 值类型的可选参数:
Sub DisplayOrder(ByVal customer As Customer,
ByVal orderID As Integer,
Optional ByVal units As Integer? = 0,
Optional ByVal backgroundColor As Color = Nothing)
End Sub
在此例中,units 的类型为 Nullable(Of Integer),backgroundColor 为非内容结构类型,但仍然将它们用作可选参数。Visual Basic 2010 还对泛型可选参数提供了更好的支持。
嵌入式互操作类型
对于执行 COM 互操作的应用程序,一个常见弱点是必须要使用主互操作程序集 (PIA)。PIA 是一种 .NET 程序集,它充当 COM 组件上的运行时可调用包装 (RCW),并具有用来标识它的唯一 GUID 。.NET 程序集与 PIA 通信,后者随后执行任何必要的封送以在 COM 和 .NET 之间移动数据。
遗憾的是,PIA 可能会使部署变得很复杂,因为它们是需要部署到最终用户计算机的附加 DLL。它们还可能会导致版本控制问题 – 例如,如果您希望应用程序能够同时依据 Excel 2003 和 Excel 2007 工作,则将需要随应用程序一起同时部署两个 PIA。
嵌入式互操作类型功能直接嵌入应用程序,但只会嵌入绝对必要的 PIA 中的类型和成员,因此无需将 PIA 部署到最终用户的计算机。
若要为现有对象启用此功能(对于新引用,默认情况已启用此功能),请在解决方案资源管理器中选择引用,并在属性窗口中更改“Embed Interop Types”选项(请参见图 5)。或者,如果使用ming令行编译器进行编译,请使用 /l(或 /link)开关,而不是 /r 和 /reference。
图 5 在解决方案资源管理器中启用嵌入式互操作类型
启用此功能之后,应用程序将不再依赖于 PIA。事实上,如果在 Reflector 或 ildasm 中打开程序集,您将注意到实际上根本没有对 PIA 的任何引用。
多重目标
Visual Basic 2010 中所有功能的最突出特点是:您甚至可以在目标为 .NET Framework 2.0 至 .NET Framework 3.5 的项目中使用这些功能。这意味着,隐式行继续符、数组文本、集合初始值设定项、语句 lambda、自动实现的属性等功能将全部都可在现有项目中使用,而不必将目标重定为 .NET Framework 4。
例外情况是嵌入式互操作类型,它依赖于只有 .NET Framework 4 中才有的类型;因此,如果将目标定为 .NET Framework 版本 2.0 至 3.5,则无法使用该功能。此外,只会采用 .NET Framework 4 中的方式对标记为变量的类型进行标记,因此,在前面的示例中,如果将目标定为版本 2.0 至 3.5,则仍然必须调用 .Cast(Of T)。不过,如果将目标定为这些早期版本,您可以建立自己的变量类型(使用 In/Out 修饰符)。
若要更改应用程序的当前目标框架,请双击“我的项目”,单击“编译”选项卡,单击“高级编译选项”,然后从底部的组合框中进行选择。
在从ming令行中进行编译时,实际上没有ming令行开关可启用此功能。实际上,编译器将查看哪个程序集提供了 System.Object 的定义(通常为 mscorlib)以及程序集的目标定为哪个框架,然后在输出程序集中标记该值。(编译器在生成 Silverlight 程序集时也使用这个同样的机制。)在使用 IDE 时,所有这些都是以透明方式进行的,因此通常您无需担心任何事情。
欢迎试用
如您所见,Visual Basic 2010 具有许多强大功能,这些功能使您能够提高工作效率,同时减少编写的代码行数,而将更多工作交给编译器来做。在本文中,我只探讨了语言功能,但 Visual Basic 2010 IDE 还有数不胜数的出色增强功能。下面列出部分增强功能:
导航到
突出显示引用
从使用中生成
更好的 IntelliSense(子字符串匹配、驼峰式大小写查找、建议模式 – 对于“首先测试”开发风ge非常有用)
多监视器支持
缩放
Visual Basic 团队渴望听到您对我们改善 Visual Basic 的工作方面的反馈,因此请在 Microsoft Connect 上将您的意见和问题发送给我们。若要了解有关语言和 IDE 功能的详细信息,请查看 msdn.com/vbasic 上的内容,其中包括文章、示例和操作方法视频。当然,最佳学习方式是深入研究和使用产品,因此是安装和试用产品的时候了。
新闻热点
疑难解答