VB.net采用的实现接口的语法是VB5发明的Implements,这个实现接口的语法在当今主流语言中独一无二。比如我有两个接口:
Interface Interface1
Sub Test()
End Interface
Interface Interface2
Sub Test()
End Interface
这两个接口有一个完全一样的成员Test。假设我需要用一个类同时实现两个接口会怎么样呢?先想想看,如果是Java,JScrip.NET这样的语言就只能用一个Test函数实现两个接口的Test成员。假如两个Test只是偶然重名,其内容必须要分别实现怎么办,于是一些解决接口重名的设计出现了……。在VB中,独特的Implements语句可以让你想怎么实现接口就怎么实现,比如下面的类Implementation用两个名字根本不一样的方法实现了两个接口。
Public Class Implementation
Implements Interface1, Interface2
Public Sub Hello() Implements Interface1.Test
End Sub
Private Sub Hi() Implements Interface2.Test
End Sub
End Class
也就是说,VB允许用任意名字的函数实现接口中的成员,而且访问器可以是任意的,比如想用Public还是Private都可以。
C#在处理重名成员上提供了显式实现(explicit implementation)的语法,其实现上述两个接口的语法为
public class Class1 : Interface1, Interface2
{
public Class1()
{
}
void Interface1.Test()
{
}
void Interface2.Test()
{
}
}
注意这里,C#只能用接口名.成员名的名字来命名实现方法,而且访问器只能是private,不能公开显式实现的方法。
在考察了IL以后,我发现.NET支持隐式实现和显式实现两种方式。其中隐式实现只要在类里面放一个与接口成员方法名字一样的方法即可——这一种VB不支持。而显式实现则在方法的描述信息里加入:
.override TestApp.Interface1::Test
无论是C#的显式实现还是VB的Implements语句都是这样的原理。也就是说.NET提供了换名实现接口成员的功能,但是只有VB将这个自由让给了用户,而其他语言还是采用了经典的语法。
在原先的VB6里,有一项奇特的功能——默认属性。在VB6中,对象的名称可以直接表示该对象的默认属性。比如TextBox的默认属性是Text,所以下面的代码
Text1.Text = "Hello"
就可以简化为
Text1 = "Hello"
这种简化给VB带来了很多麻烦,赋值运算就需要两个关键字——Let和Set,结果属性过程也需要Let和Set两种。而且这种特征在后期绑定的时候仍能工作。到了VB.NET,这项功能被大大限制了,现在只有带参数的属性才可以作为默认属性。如
List1.Item(0) = "Hello"
可以简化为
List1(0) = "Hello"
这种语法让有默认属性的对象看起来像是一个数组。那么VB怎么判断一个属性是否是默认属性呢?看下列代码
Public Class PropTest
Public Property P1(ByVal index As Integer) As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
Default Public Property P2(ByVal index As Integer) As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
End Class
P1和P2两个属性基本上完全相同,唯一的不同是P2带有一个Default修饰符。反汇编这个类以后,可以发现两个属性完全相同,没有任何差异。但是PropTest类却被增加了一个自定义元属性System.Reflection.DefaultMemberAttribute。这个元属性指定的成员是InvokeMember所使用默认类型,也就是说后期绑定也可以使用默认属性。可是我试验将DefaultMember元属性手工添加到类型上却不能达到让某属性成为默认属性的功能。看来这项功能又是VB的一项“语法甜头”。但是,VB或C#的编译器对别人生成的类的默认属性应该只能通过DefaultMemberAttribute来判断,所以我将一个VB类只用DefaultMemberAttribute指定一个默认方法,不使用Default,然后将它编译以后给C#用,果然,C#将它识别为一个索引器(indexer)!
既然说到了C#的索引器,我们就顺便来研究一下VB和C#属性方面的不同。刚才的实验结果是VB的默认属性在C#中就是索引器。但是VB仍然可以用属性的语法来访问默认属性,而C#只能用数组的语法访问索引器。更特别的是,VB可以创建不是默认属性,但是带有参数的属性,如上面例子里的P1,而C#则不支持带参数的属性,如果将VB编写的,含有带参数属性的类给C#用,C#会提示“属性不受该语言支持,请用get_XXX和set_XXX的语法访问”。也就是说,带参数的属性是CLR的一项功能,但不符合CLS(通用语言规范),因此就会出现跨语言的障碍。这也更加深了我们对CLS的认识——如果你希望让你的代码跨语言工作,请一定要注意符合CLS。