C＃与Visual Basic .NET的比较

C#和VB.NET是微软公司开发的.NET框架中，两种面向对象的编程语言，分别衍生自C++语言及Visual Basic，因此在语法，注解方式都有许多的差异。

## 语言历史

C#和VB.NET各自有其不同的历史，在语法上也有很大的差异。C#语法的基础是衍生自1970年代丹尼斯·里奇在贝尔实验室（AT&T）发展的C语言，其中也包括了完全物件导向的C++，许多Java语言语法的基础也是来自C++语言，这也是C#和Java有许多相近之处的原因。

VB.NET是来自1960年代的BASIC语言，是“Beginner's All-purpose Symbolic Instruction Code”的缩写，一开始BASIC在学校中教授，而且如它的名字所述的，被视为是一个基础的程式语言。1970年代微电脑的制造商将BASIC用来做电脑系统的程式语言，以及有简单输入输出控制的半操作系统。1980年代微软也开始使用将一此语言，1990年代发展为Visual basic，用在window系统的快速应用程序开发，Visual basic打败了当时像PowerBuilder等快速应用程序开发工具。虽然Visual Basic如此的成功，但在2000年代初期微软提出.NET框架及对应的Visual Studio开发平台，而Visual Basic也在第六版（VB6）画下句点。

## 语言比较

虽然C#和VB.NET在语法上有相当的不同，但因为都是微软开发的.NET框架下的程式语言，而且是由同一个开发团队进行开发、管理及维护，因此会有不少的共通点。C#和VB.NET会编译为同一种中介语言，在同一种.NET框架下的执行期函式库下执行。C#和VB.NET在语言结构上有一些差异，差异主要都是语法上的差异，但不要使用微软提供，方便和VB6转换的相容性函式库，几乎每一个VB.NET中的指令都有对应的C#的指令，反之亦然。为了延伸其机能，两种语言也参考同一个.NET框架下的基础类别。因此大多数用其中一个语言写的程式可以用语法转换程式转换为另一种语言，只有少数例外，而已有许多开源软件及商业软件有这类的功能。

### 语言特性

从技术角度来看，C＃和VB.NET之间的大部分差异都是语法糖。也就是说，大多数特性都存在于两种语言，但有些任务在一种语言中比另一种语言更容易完成。

#### 存在于Visual Basic .NET但不存在于C#的特性

- 可以使用`WithEvents`结构声明变量。此结构令程序员可以从“类名”下拉列表中选择一个对象，然后从“声明”下拉列表中选择一个方法以自动插入方法签名。
- 自动连接事件。VB.NET支持事件的`Handles`语法，它将事件处理程序连接到对象变量而不是对象。
- 使用`RaiseEvent`关键字引发事件，IDE在此关键字后会显示可用的事件列表。`RaiseEvent`隐式检查事件处理程序是否为Nothing。（在C＃中引发事件在语法上与调用过程完全相同，并且需要额外的代码行来检查有线事件处理程序）
- 事件代理被隐式声明。
- 在`With ... End With`结构内，点操作符前可不写出对象名。
- XML文本
- date文本常量，如`#12/31/2000#`
- `Module`语句（虽然C#的静态类更容易理解，但静态类里每个成员都必须显示声明为`static`。）
- 当前文件导入模块后，该模块的成员可以无需限定符进行访问。
- `My`命名空间
- COM组件和互操作性更强，因为对象类型是运行时绑定的。C# 4.0的`dynamic`关键字提供了相同的便利。
- 可以在项目级别导入命名空间，这样一来该项目的每个文件都可访问该命名空间。
- 使用`#Const`指令定义条件编译器常量。`#Const`常量一般在`#If`语句中使用。
- 属性可以携带参数。
- 属性可以直接按引用（`ByRef`）传入方法。在C#里属性必须先赋值给局部变量，再传入。
- 接口里可定义枚举。
- `Case`语句可接不等式，如`Select x: Case Is >= 3`。(在C#里必须用`if ... else`实现。)
- 子类若有与父类同名的方法，则子类方法必须被`Overloads`关键字修饰。(C＃中缺少此关键字可能会导致意外重载)
- `Like`运算符支持模式比较。（C#只能访问`Microsoft.VisualBasic.CompilerServices.LikeOperator.LikeString`方法)
- `Return`语句可省略。也可向当前方法赋值来设置返回值。
- Visual basic有不少内置常量，如`vbCrLf`、`vbTab`。
- 没有用于修饰参数的`out`关键字。
- `MyClass`关键字的行为类似于引用最初实现的类的当前实例。`MyClass`类似于`Me`，但是对它的所有方法调用都被视为方法被NotOverridable修饰。
- `MyBase.New`用来调用父类的构造函数。
- 局部变量自动初始化。
- 方法内的静态局部变量在方法调用多次时可保持原来的值。
- `End`语句直接终止程序。
- 可对Lambda表达式推断类型，如`Dim x = Function(n As Integer) n + 1`
- 成员名可以与其所在的类型名相同。

#### 存在于C#但不存在于Visual Basic .NET的特性

- 多行注释。
- 静态类。
- C#和VB都可以在项目属性里开启或禁用溢出检查，但C#提供`checked`、`unchecked`关键字进行精细控制。
- for语句可以有多个条件，如`for(int i = 0; i < 10 && somethingTrue; i++)`。
- 属性的getter和setter可以实现不同的接口。VB里必须定义两个属性。
- 隐式接口实现。
- null条件运算符`??`返回第一个非null值（如`null ?? null ?? 1`返回`1`）。
- 可在`unsafe`环境使用指针
- 条件运算符`?`: `someThingTrueOrFalse ? whenTrue() : whenFalse();`

#### 存在于Visual Basic .NET但无法应用到C#的特性

- 转换布尔值True到Integer可能会产生-1或1，具体取决于所使用的转换。
- 变量赋值和比较使用相同的符号：`=`。C#用不同的符号：`==`和`=`，这允许在条件语句中先赋值，再比较。
- VB.NET标识符不区分大小写。
- 当为具有不同数据类型的变量赋值（并且`Option Strict`未打开时），VB.NET将尽可能强制转换该值。这种自动强制转换有时会导致意想不到的结果，比如

```vb
Dim i As Integer = "1" '编译器插入一个从String到Integer的转换方法
Dim j As String = 1 '编译器插入一个从Integer到String的转换方法
If i = j Then '编译器插入一个从String到double的转换方法
    MsgBox("Avoid using, but this message will appear!")
End If
```

为了向后兼容，`Option Strict`的默认值是关闭，但微软建议将其开启

- 内置函数`Val()`的参数可以是null。
- 有大量内置的类型转换函数：CInt, CStr, CByte, CDbl, CBool, CDate, CLng, CCur, CObj。

#### 存在于C#但无法应用到Visual Basic .NET的特性

- C＃标识符区分大小写。
- 可将同一值赋给多个变量：`a=b=c`。

## 两种语言的HelloWorld

以下是两种语言的HelloWorld：

VB.NET

```vbnet
Imports System

Module HelloWorldApp
    ' VB.NET Console 主程序
    Sub Main()
        ' 輸出 Hello World! 至 Console
        Console.WriteLine("Hello World!")
        ' ReadKey 用來暫停避免直接關閉 Console 視窗
        Console.ReadKey()
    End Sub
End Module
```

C#

```csharp
using System;

namespace HelloWorldApp
{
    class Program
    {
        // C# Console 主程序
        static void Main(string[] args)
        {
            // 輸出 Hello World! 至 Console
            Console.WriteLine("Hello World");
            // ReadKey 用來暫停避免直接關閉 Console 視窗
            Console.ReadKey();
        }
    }
}
```