源文件约定
我们需要知道,两种语言在源程序的文件命名约定和结构上有一些不同:
文件命名
包含 C# 类的文件的命名约定与 Java 有点不同。首先,在 Java 中,所有源文件的扩展名都为 .java。每个源文件都包含一个顶层公共类声明,并且类名必须与文件名相匹配。换句话说,一个用公共范围声明的名为 Customer 的类必须定义在具有名称 Customer.java 的源文件中。
而 C# 源代码是由 .cs 扩展名表示的。与 Java 不同,源文件可以包含多个顶层公共类声明,而文件名不需要与任何类名相匹配。
顶层声明
在 Java 和 C# 中,源代码以按一定顺序排列的顶层声明开始。Java 和 C# 程序中的声明只存在少许差别。
Java 中的顶层声明
在 Java 中,我们可以用 package 关键字将类组合在一起。打包的类必须在源文件的第一个可执行的行中使用 package 关键字。接着出现的是需要访问其他包中的类的任何导入语句,之后是类声明,比如:
package ;
import .;
class Customer
{
...
}
C# 中的顶层声明
C# 使用命名空间的概念,通过 namespace 关键字将逻辑上相关的类组合在一起。这些做法类似于 Java 包,而具有相同名称的类可以出现在两个不同的命名空间中。要访问定义在当前命名空间之外的命名空间中的类,我们可以使用紧跟该命名空间名的 using 关键字,如下所示:
using .;
namespace
{
class Customer
{
...
}
}
注意,using 语句可以完全合法地放在命名空间声明中,在这种情况下,这样导入的命名空间就形成了包含命名空间的一部分。
Java 不允许在相同的源文件中有多个包,而 C# 允许在一个 .cs 文件中有多个命名空间:
namespace AcmeAccounting
{
public class GetDetails
{
...
}
}
namespace AcmeFinance
{
public class ShowDetails
{
...
}
}
完全限定名和命名空间别名
同 Java 一样,通过提供类的完全限定名(如 System.Data.DataSet 或上面的示例中的 AcmeAccounting.GetDetails),我们可以在没有命名空间的 using 引用的情况下访问 .NET 或用户定义的命名空间中的类。
完全限定名可能会变得很长而不便于使用,在这种情况下,我们可以使用 using 关键字来指定一个简称或别名,以提高代码的可读性。
在下面的代码中,创建了一个别名来引用由一个虚构的公司所编写的代码:
using DataTier = Acme.SQLCode.Client;
using System;
public class OutputSales
{
public static void Main()
{
int sales = DataTier.GetSales("January");
Console.WriteLine("January's Sales: {0}", sales);
}
}
注意 WriteLine() 的语法,格式字符串中带有 {x},其中 x 表示在此处要插入的值的参数列表的位置。假定 GetSales() 方法返回 500,则该应用程序的输出将为:
January's Sales: 500
预处理指令
与 C 和 C++ 相似,C# 包括预处理器指令,预处理器指令提供了有条件地跳过源文件的某些部分、报告错误和警告条件,以及描述源代码的不同部分的能力。使用“预处理指令”这个术语只是为了与 C 和 C++ 编程语言保持一致,因为 C# 并不包括单独的预处理步骤。有关 C# 预处理器指令的完整列表,请参见 C# 预处理器指令。
语言语法
在这一部分中,我们讨论这两种语言之间的相似点和不同点。一些主要的不同点有:
常量声明― Java 为此而使用 final 关键字,而 C# 使用关键字 const 或 readonly。
复合数据类型― 在 Java 中,我们可以使用类关键字来创建作为没有方法的类的复合数据类型,但是 C# 为此提供了 struct,同 C 中一样。
析构函数― C# 允许我们创建在销毁类的实例之前调用的析构函数方法。在 Java 中,可以提供 finalize() 方法来包含在将对象作为垃圾回收之前清除资源的代码。在 C# 中,由类析构函数来提供此功能。析构函数类似一个没有参数并前面带有波形符“~”的构造函数。
函数指针 ― C# 提供一个称为 delegate 的构造来创建类型安全的函数指针。Java 没有任何与之对等的机制。
数据类型
C# 提供了在 Java 中可用的所有数据类型,并且增加了对无符号数和新的 128 位高精度浮点类型的支持。
在 Java 中,对于每个基本数据类型,核心类库都提供了一个包装类来将其表示为 Java 对象。例如,Integer 类包装 int 数据类型,而 Double 类包装 double 数据类型。
而在 C# 中,所有的基本数据类型都是 System 命名空间中的对象。对于每个数据类型,都提供一个简称或别名。例如,int 是 System.Int32 的简称,而 double 是 System.Double 的简写形式。
下面的列表给出了 C# 数据类型及其别名。可以看到,前 8 个对应于 Java 中可用的基本类型。不过,请注意,Java 的 boolean 在 C# 中称为 bool。
因为 C# 将所有的基本数据类型都表示为对象,所以按照基本数据类型来调用对象方法是可能的。例如:
int i=10;
Console.WriteLine(i.ToString());
借助于自动装箱和拆箱,可以达到此目的。更多信息请参见装箱和拆箱。
枚举
与 C/C++ 相似,在 C# 中可以使用枚举来组合已命名常量,而在 Java 中不能使用枚举。下面的示例定义了一个简单的 Color 枚举。
public enum Color {Green, Orange, Red, Blue}
还可以为枚举赋整数值,如下面的枚举声明所示:
public enum Color {Green=10, Orange=20, Red=30, Blue=40}
下面的程序调用 Enum 类型的 GetNames 方法来显示枚举的可用常量。然后,它将值赋给枚举,并显示该值。
using System;
public class TypeTest
{
public static void Main()
{
Console.WriteLine("Possible color choices: ");
//Enum.GetNames returns a string array of named constants for the enum
foreach(string s in Enum.GetNames(typeof(Color)))
{
Console.WriteLine(s);
}
Color FavoriteColor = Color.Blue;
Console.WriteLine("Favorite Color is {0}",FavoriteColor);
Console.WriteLine("Favorite Color value is {0}", (int)FavoriteColor);
}
}
在运行之后,该程序将显示如下结果:
Possible color choices:
Green
Orange
Red
Blue
Favorite Color is Blue
Favorite Color value is 40
字符串
在 Java 和 C# 中,字符串类型表现出相似的行为,只有一些细微的差别。二者的字符串类型均是不可改变的,这意味着一旦字符串创建完毕,就不能改变字符串的值。在二者的实例中,看起来像修改字符串实际内容的方法实际上创建一个新的字符串供返回,而保留原始的字符串不变。在 C# 和 Java 中,比较字符串值的过程是不同的。在 Java 中,为了比较字符串的值,开发人员需要按照字符串类型调用 equals() 方法,正如在默认情况下 == 运算符比较引用类型一样。在 C# 中,开发人员可以使用 == 或 != 运算符来直接比较字符串的值。在 C# 中,尽管字符串是引用类型,但是在默认情况下,== 和 != 运算符将比较字符串的值而不是引用。在本文后面,我们将讨论值类型和引用。
正如在 Java 中一样,C# 开发人员不应该使用字符串类型来连接字符串,以避免每次连接字符串时都创建新的字符串类的开销。相反,开发人员可以使用 System.Text 命名空间中的 StringBuilder 类,它在功能上等同于 Java 中的 StringBuffer 类。
字符串
C# 提供了避免在字符串常量中使用转义序列(如代表制表符的“\t”或代表反斜杠字符的“\”)的功能。要这样做,可以在为字符串赋值之前使用 @ 符号来声明字符串。下面的示例显示了如何使用转义字符以及如何为字符串赋值:
//Using escaped characters
string path = "\\\\FileShare\\Directory\\file.txt";
//Using String Literals
string escapedPath = @"\\FileShare\Directory\file.txt";
转换和强制转换
Java 和 C# 遵守相似的数据类型自动转换和强制转换规则。
同 Java 一样,C# 既支持隐式类型转换又支持显式类型转换。在扩大转换的情况下,转换是隐式的。例如,下面从 int 到 long 的转换是隐式的,如同 Java 中的一样:
int intVariable = 5;
long l = intVariable;
下面是 .NET 数据类型之间的隐式转换列表:
可以使用与 Java 一样的语法对希望显式转换的表达式进行强制转换:
long longVariable = 5483;
int intVariable = (int)