VB.net 是怎样做到的(一)
VB.net 能够实现很多 C# 不能做到的功能,如 When 语句、 Optional 参数、局部 Static 变量、对象实例访问静态方法、 Handles 绑定事件、 On Error 处理异常、 Object 直接后期绑定等等。 VB 和 C# 同属 .net 的语言,编译出来的是同样的 CIL ,但为什么 VB 支持很多有趣的特性呢。我们一起来探究一下。
首先是局部 Static 变量。 VB 支持用 Static 关键字声明局部变量,这样在过程结束的时候可以保持变量的数值:
Public Sub Test1()
** Static ** ** i As Integer **
i += 1 ' 实现一个过程调用计数器
End Sub
我们实现了一个简单的过程计数器。每调用一次 Test ,计数器的数值就增加 1 。其实还有很多情况我们希望保持变量的数值。而 C# 的 static 是不能用在过程内部的。因此要实现过程计数器,我们必须声明一个类级别的变量。这样做明显不如 VB 好。因为无法防止其他过程修改计数器变量。这就和对象封装一个道理,本来应该是一个方法的局部变量,现在我要被迫把它独立出来,显然是不好的设计。那么 VB 是怎么生成局部静态变量的呢?将上述代码返汇编,我们可以清楚地看到在 VB 生成的 CIL 中, i 不是作为局部变量,而是作为类的 Field 出现的:
.field private specialname int32 $STATIC$Test1$2001$i
也就是说, i 被改名作为一个类的字段,但被冠以 specialname 。在代码中试图访问 $STATIC$Test1$2001$i 是不可能的,因为它不是一个有效的标识符。但是在 IL 中,将这个变量加一的代码却与一般的类字段完全一样,是通过 ldfld 加载的。我觉得这个方法十分聪明,把静态变量变成生命周期一样的类字段,但是又由编译器来控制访问的权限,让它成为一个局部变量。同时也解释了 VB 为什么要用两个不同的关键字来声明静态变量—— Static 和 Shared
VB.net 是怎样做到的(二)
VB.net 支持一项很有意思的功能—— MyClass 。大部分人使用 MyClass 可能仅限于调用本类其他构造函数时。其实 MyClass 可以产生一些很独特的用法。 MyClass 永远按类的成员为不可重写的状态进行调用,即当类的方法被重写后,用 MyClass 仍能得到自身的版本。下面这个例子和 VB 帮助中所举的例子非常相似
Public Class MyClassBase
Protected Overridable Sub Greeting()
Console.WriteLine("Hello form Base")
End Sub
Public Sub UseMe()
** Me ** .Greeting()
End Sub
Public Sub UseMyClass()
** MyClass ** .Greeting()
End Sub
End Class
Public Class MyClassSub
Inherits MyClassBase
Protected Overrides Sub Greeting()
Console.WriteLine("Hello form Sub")
End Sub
End Class
我们用一段代码来测试:
Dim o As MyClassBase = New MyClassSub()
o.UseMe()
o.UseMyClass()
结果是 UseMe 执行了子类的版本,而 UseMyClass 还是执行了基类本身的版本,尽管这是一个虚拟方法。观其 IL ,可以看到其简单的实现原理:
Me 用的调用指令是 callvirt
IL_0001: callvirt instance void App1.MyClassBase::Greeting()
而 MyClass 调用的是 call
IL_0001: call instance void App1.MyClassBase::Greeting()
奇怪的是,如此简单的一个功能,我竟然无法用 C# 实现, C# 怎样也不允许我按非虚函数的方式调用一个虚函数。 C++ 可以用类名 :: 方法名的方式访问自身版本的函数,但 C# 的类名只能用来访问静态的成员。这真是 C# 一个奇怪的缺陷。