代码换肤术(一)——C#和VB

代码换肤术(一)—— C# 和 VB

现在流行程序“换肤术”,就是把操作界面变个样子,程序当然还是原来的程序。代码的移植也可以称为一种“换肤术”,内容不改,但变成了另一种语言。本文介绍的是从当今 最 火热的 C# 与 VB.net 间移植的技巧。

按理说,这两种语言没有什么移植的必要,因为他们生成的代码可以完全通用。但是如果一个工程基本上是 VB 写成的,却需要少许已经存在的 C# 过程,用组件并不是一种效率很高的办法。就算是学习 C# 或 VB ,学会他们之间的移植可以双倍的利用已经存在的代码(如好玩的 Donkey.net 就只有 VB 版)。

有人比较过这两种语言,得出的结论是他们很相似。但即便是这样, VB 初学者看到诸如 ( ( Button ) sender).Text = “ 启动 ”; 之类的语法不知道如何移植到 VB ,而 C# 初学者看到 Handles Button1.Click 等语法也会为移植而感到头痛。下面就看看他们最难移植的部分:

1 、 Option 语句。 VB 的 Option 语句可以打开或关闭变量声明检查和类型转换检查。尤其是 Option Strict 被关闭后, VB 变成弱类型语言,很多类型转换都是自动的,移植到 C# 中会产生无数错误。因此,如果要将带有 Option Strict Off 语句的 VB 程序移植到 C# ,最好先在 VB 中将 Option Strict 打开,然后把所有类型转换出错的地方变成强类型转换,然后再进行移植。

2 、类型转换。 VB 提供了很多类型转换函数型运算符,如 CInt(), CSng(), CStr() 等,在 C# 中只要用 (int) , (float), (String) 即可。然而如果不是标准类型,如下面的 C# 语句:

((System.Button)sender).Text = “ 启动 ”;

就要使用 VB 的函数型运算符 CType 来实现。上面的代码正确的移植方法是:

CType(sender, System.Button).Text = “ 启动 ”

千万不要使用某些人推荐的,将 Option Strict 关闭,然后用后期绑定调用 sender 对象的方法,这根本不符合程序移植不能改变本质的要求。

3 、修饰符和属性标签。 VB 和 C# 的修饰符是完全对等存在的,但是拼写往往不同,给移植带来了很多麻烦,尤其是表示相同意思的关键字从字面理解完全不同的时候。下面就给出了 VB 和 C# 对应的关键字:

VB

|

C#

|

VB

|

C#

---|---|---|---

** Inherits **

|

** : **

|

** Implements **

|

** : **

** MustInherit **

|

** abstract **

|

** NotInheritable **

|

** sealed **

** Overridable **

|

** virtual **

|

** NotOverridable **

|

** sealed **

** MustOverride **

|

** abstract **

|

** Overrides **

|

** override **

** [Overloads] **

|

|

** Shadows **

|

** new **

** Shared **

|

** static **

|

** Public **

|

** public **

** Protected **

|

** protected **

|

** Friend **

|

** internal **

** Protected Friend **

|

** protected internal **

|

** Private **

|

** private **

** Static **

|

用别的方法实现

|

** ByVal **

|

** ByRef **

|

** ref **

|

** Optional **

|

** ParamArray **

|

** params **

|

无法实现

|

** unsafe **

无法实现

|

** fixed **

| |

可以看出, VB 的关键字比较长,而且使用上也比 C# 更加严格。从 C# 向 VB 移植的时候,要分外注意哪些 VB 有而 C# 没有的关键字以及在 C# 拼写相同,在 VB 中拼写不同的关键字(如 MustOverride 和 MustInherit )。有些关键字如 unsafe ,如果 C# 使用了他们,将无法移植到 VB 中。好在这些关键字在商业应用中并不常用。

属性标签在这两种语言中非常的相似,移植上应该没有任何难度,只要知道在 C# 中用方括号 [] 表示属性标签,而在 VB 中用的是尖括号 <> 。另外,如果要用名称结合传递参数, C# 直接使用 = 号,而 VB 使用 := (冒号和等号)。

4 、委派类型。委派类型就是安全的函数指针类型。在 C# 中,难以分辨是函数指针在工作还是函数本身在工作,因为他们的语法相同。当要为一个委派类型的变量复制的时候,直接等于一个函数即可,如:

public delegate void FuncType(Object e)

...

FuncType func;

func = new FuncType(this.SampleFunction1);

// 调用

func(something);

// 换指向另外一个函数

func = this.SampleFunction2

然而 VB 中,委派类型就像是一般的对象,有它的方法,与函数本身明显不同。你不能将过程的名字直接赋给一个委派类型的对象,而必须使用 AddressOf 运算符,下面的例子就是上文 C# 程序的 VB 版,注意那些实现不同的地方:

Public Delegate Sub FuncType(ByVal e As Object)

...

Dim func As FuncType

func = New FuncType( AddressOf Me.SampleFunc1)

‘ 调用

func. Invoke (something)

‘ 换指向另外一个函数

func = AddressOf Me.SampleFunction2

5 、事件处理。这是两种语言最大的差别之一, VB 传承以前版本强大的事件处理机制,许多语法都比 C# 更加灵活。好在无论什么情况,他们之间都是可以互相移植的。

对于事件定义,两种语言都是一个委派类型加一个事件属性,如:

[C#]

public delegate void MyEventHandler(Object sender, EventArgs e);

public event MyEventHandler MyEvent;

[Visual Basic]

Public Delegate Sub MyEventHandler(ByVal sender As Object, ByVal e As EventArgs)

Public Event MyEvent As MyEventHandler

VB 还支持另外一种更加紧凑的定义方法,只有一条语句:

Public Event MyEvent(ByVal sender As Object, ByVal e As EventArgs)

移植的时候,要把参数部分分离出来,成为一个委派类型,再按照普通方法定义事件即可。

关于事件响应, C# 与 Delphi 等语言一样,是动态绑定事件过程的,其语法类似于下:

internal MyClass myobj;

...

myobj = new MyClass();

...

** myobj.MyEvent += this.myobj_MyEvent; **

...

protected void myobj_MyEvent(Object sender, EventArgs e)

{

// 语句

}

可以看到, C# 是利用运算符连接事件过程和事件属性的。之后,还可以用 -= 运算符解除事件过程与事件属性的绑定。 VB 不支持运算符重载,但仍然支持这种动态绑定的事件过程,方法是使用 AddHandler 和 RemoveHandler 关键字。如上面黑体部分可以移植为:

** AddHandler ** myobj.MyEvent , AddressOf Me.myobj_MyEvent ****

解除绑定的语法与此类似,只是关键字为 RemoveHandler 而已。一定不要忘记过程前面还有一个 AddressOf 关键字!

动态绑定的事件过程工作起来比较慢, VB 支持一种更快的静态绑定事件过程。一旦为对象设置了静态的事件过程,就不能解除绑定,一般大多数情况都是如此。语法如下:

‘ 定义变量时使用 WithEvents 关键字

Friend WithEvents myobj As MyClass

‘ 直接书写事件过程,注意 Handles 的语法:

Protected Sub myobj_ MyEvent( ByVal sender As Object, ByVal e As EventArgs) _

** Handles myobj.MyEvent **

‘ 语句

End Sub

它表示 myobj_MyEvent 这个过程仅仅响应 myobj.MyEvent 这个过程。如果一个过程要响应很多个事件,把他们列在 Handles 后面,用逗号隔开,如 Handles Event1, Event2, ...

遇到这种情况,要看清 Handles 后面的所有对象和事件,将它们一一改写成动态绑定的语句:

Protected Sub XXX( ...) Handles myobj1.MyEvent , myobj2.MyEvent

==>

** myobj1.MyEvent ** += this.XXX;

** myobj2.MyEvent ** += this.XXX;

...

protected void XXX( ...){}

当事件 比较多时, C# 显著变得比较麻烦,幸好一个过程响应一大堆事件的情况也不太多(不过我就编写过一个过程相应 8 个事件,移植起来好麻烦!)。原则上说,将静态事件过程移植为动态事件过程并没有完全遵守移植的规定,但我估计他们实现的原理不会相差太多,所以也不用担心。

6 、异常处理。 VB 支持两种形式的异常,即 .net 框架的异常和 VB 自己的错误号码。而 C# 只支持第一种。用到 VB 自己的错误号码的程序几乎无法移植到 C# 中,所以应该尽量使用 .net 框架的异常,如下面 VB 语句:

Try

‘ 发生错误的代码

Catch When Err.Number = 52

‘ 解决错误的代码

End Try

这段代码无法直接移植到 C# 中,只有用 Exception 对象取代 Err 对象获得异常信息,才能顺利移植。另外 VB 的 When 语句带给 Try 语句十分灵活的用法,必须用很高的技巧才能在 C# 中实现,这就需要具体问题具体分析了。

VB 支持 Exit Try 语句,可以直接从 Try 块或 Catch 块跳转到 Finally 块。 C# 没有提供类似的语法,可以用以下技巧:

[Visual Basic]

Try

‘ 一些语句

Exit Try

Finally

‘ 一些语句

End Try

[C#]

try

{

// 一些语句

goto __leave;

}

finally

{

// 一些语句

}

__leave: // 别忘了这里还有哦! ** **

总之是利用了 finally 块无法 跳过的特征,用 goto 语句模拟了 Exit Try 语句。

如果 VB 程序用的是 VB 特有的 On Error GoTo 语句实现的错误处理,问题就麻烦了。代码可能在过程中上下跳跃,无法预料语句的执行方式。这种代码理解起来就头痛,更不要说移植了。总体来说,把所有语句统统转移到 try 块中,然后用 catch 一一处理错误。遇到要返回( Resume 语句)的时候,只好 Copy 代码了。反正不是一件容易的事情,慢慢改就是了。

7 、模块。 VB 支持模块, C# 不支持。但也没有关系,只要在 C# 中制造一个 abstract 类,共享所有成员,就和模块一样了。当然不能像 VB 一样直接访问模块中的成员,需要用到“类名 . 成员名”的用法。

8 、接口。 C# 在接口方面也没有 VB 强大(怎么这么重要的功能也 不 做得好一点?), VB 采用 Implements 语句结合接口的成员和类的实现成员,而 C# 是用名称结合。因此 VB 就可以随便修改实现成员的访问级别和名称,而 C# 就不能改名称。将 C# 移植为 VB 时,最好利用 VB.net 编辑器直接实现接口,比较简单。把 VB 移植为 C# 时,就必须把改过的名字都改回来,遇到名字冲突就更讨厌了(这时候我几乎不想再移植为 C# 了)。给一个例子:

[Visual Basic]

Public Class Class1 : Implements IMyInterface

Public Sub DoSth ( ) Implements IMyInterface.Method1

End Sub

End Class

[C#]

public class Class1 : IMyInterface

{

public void Method1()

{

}

}

9 、运算符重载。这会遇到 VB 头痛了,既然 VB 不支持运算符重载,那么就必须用子程序和函数来模拟运算符。比如建立 Plus 和 Minus 方法模拟 + 和 - 的运算。当然还是有很多情况(比如遇上了 explicit 和 implicit 语句)就真的没有办法了,只好不移植了。运算符重载是一个很不错的功能,它能使很多操作简单地完成,如果 VB 支持它,就真的是完美语言了。

好了,想必最麻烦的地方已经说完了,剩下的就是简单的 Copy 了。虽然有些地方还没有说清楚,但基本上阐明了两种语言的不同(一看,不同还挺多的吧),反正也不用移植大的工程,了解这些内容主要是为了利用双倍的利用已经存在的代码,但愿本文对你有用。由于水平低劣,如有错误请各位大虾指正,小弟必洗耳恭听。

Published At
Categories with Web编程
Tagged with
comments powered by Disqus