由于 C# 是在编译时静态类型化的,因此变量在声明后就无法再次声明,或无法分配另一种类型的值,除非该类型可以隐式转换为变量的类型。 例如, string
无法隐式转换为 int
。 因此,声明 i
为 an int
后,无法向其分配字符串“Hello”,如以下代码所示:
int i;
// error CS0029: can't implicitly convert type 'string' to 'int'
i = "Hello";
但是,有时可能需要将值复制到另一种类型的变量或方法参数中。 例如,你可能有一个整数变量,需要传递给其参数类型为 double
的方法。 或者,可能需要将类变量分配给接口类型的变量。 这种类型的操作称为 类型转换。 在 C# 中,可以执行以下类型的转换:
隐式转换:不需要特殊语法,因为转换始终成功,并且不会丢失任何数据。 示例包括从较小到较大的整型类型的转换、从派生类到基类的转换,以及范围转换。
显式转换(强制转换):显式转换需要 强制转换表达式。 在转换中可能丢失信息时或在出于其他原因转换可能不成功时,必须进行强制转换。 典型示例包括数值转换为精度较低或范围较小的类型,以及基类实例到派生类的转换。
用户定义的转换:用户定义的转换使用特殊方法,可以定义这些方法,以便在没有基类派生类关系的自定义类型之间启用显式和隐式转换。 有关详细信息,请参阅用户定义转换运算符。
帮助程序类的转换:若要在不兼容的类型(如整数和 System.DateTime 对象)或十六进制字符串和字节数组之间进行转换,可以使用 System.BitConverter 类、 System.Convert 类和
Parse
内置数值类型的方法,例如 Int32.Parse。 如需了解更多信息,请参阅以下文章:
隐式转换
对于内置数值类型,当要存储的值可以容纳到变量中时,可以进行隐式转换,而无需截断或舍入。 对于整型类型,此限制意味着源类型的范围是目标类型的适当子集。 例如, 长 型(64 位整数)的变量可以存储 int (32 位整数)可以存储的任何值。 在下面的示例中,编译器首先将右侧的值 num
隐式转换为类型 long
,然后将其赋值给 bigNum
。
// Implicit conversion. A long can
// hold any value an int can hold, and more!
int num = 2147483647;
long bigNum = num;
有关所有隐式数值转换的完整列表,请参阅内置数值转换文章的隐式数值转换部分。
对于引用类型,总是存在从一个类到其任何一个直接或间接基类或接口的隐式转换。 不需要特殊语法,因为派生类始终包含基类的所有成员。
Derived d = new Derived();
// Always OK.
Base b = d;
显式转换
但是,如果无法在没有丢失信息的风险的情况下进行转换,编译器要求你执行显式转换,这称为 强制转换。 强制转换是显式进行转换的一种方式。 表示您意识到可能会发生数据丢失,或者强制转换在运行时可能会失败。 若要执行强制转换,请在要转换的表达式之前在括号中指定目标类型。 以下程序将双精度浮点数强制转换为整数。如果没有进行强制转换,程序将无法编译。
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
Console.WriteLine(a);
// Output: 1234
有关支持的显式数值转换的完整列表,请参阅内置数值转换文章的“显式数值转换”部分。
对于引用类型,如果需要从基类型转换为派生类型,则必须进行显式强制转换:
引用类型之间的强制转换不会更改基础对象的运行时类型,只会更改用作对该对象的引用的值的类型。 有关详细信息,请参阅 多态性。
运行时出现的类型转换异常
在某些引用类型转换中,编译器无法确定类型转换是否有效。 正确进行编译的强制转换操作有可能在运行时失败。 如下面的示例所示,类型转换在运行时失败将导致引发 InvalidCastException。
Animal a = new Mammal();
Reptile r = (Reptile)a; // InvalidCastException at run time
显式地将参数 a
转换为 Reptile
是一种危险的假设。 不要做假设,检查类型更为安全。 C# 提供 is
运算符,使你可以在实际执行强制转换之前测试兼容性。 有关详细信息,请参阅如何使用模式匹配以及 as 和 is 运算符安全地进行强制转换。