强制转换和类型转换是把表达式从一种数据类型变成另一种的两种不同方式。
C# 文档把类型转换归类为使用as 运算符或is 运算符。而强制转换则定义为使用强制转换运算符。
在 C# 中,除了数值和类层次结构之外,很多时候你都得通过调用“目标”类型的某个成员来完成转换,比如用 Int32.Parse() 把字符串转成整数,或者调用“源”类型的成员,例如 object.ToString()。使用 JavaScript 等动态语言的开发者要特别注意这一点。
请注意,还有隐式和显式的强制转换运算符(在 operator-overloading 中讨论),它们可以让你为自己的类型实现相当自由的强制转换。
C# 的类型系统比 C 或 JavaScript 要严格一些,因此强制转换操作也受到更多限制。只要“目标”类型能够保留“源”类型值的范围和符号,隐式强制转换就可以在两个数值类型之间进行。请注意文档中关于转换为实数时可能丢失精度的例外说明。
隐式强制转换不需要任何特殊语法。例如:
int myInt = 1729;
long myLong = myInt;
数值(或字符串)表达式不能隐式转换为 bool。为此,基类库(BCL)提供了 Convert.ToBoolean()。
类型为 char 的表达式可以隐式强制转换为 int。反方向的强制转换则必须是显式的。并非所有 int 值都是合法的 utf 16 字符。
当数值类型无法隐式强制转换时,你通常可以使用显式的强制转换运算符。
如果被转换的值因为宽度不够或存在符号冲突而无法用“目标”类型表示,那么对于整数可能会抛出溢出异常,而对于 float 和 double,目标类型的变量可能会取值为 Infinity。
类型为 int 的表达式可以显式强制转换为 char。这可能会得到一个非法的 char。
int largeInt = Int32.MaxValue;
int largeNegInt = Int32.MinValue;
ushort shortUnsignedInt = ushort.MaxValue;
float largeFloat = float.MaxValue;
float smallFloat = 17.29f;
// implicit cast
int from_ushort = shortUnsignedInt; // 65535
float from_int = largeInt; // -21474836E+09
int from_char = 'a'; // 96
// explicit cast
uint from_largeInt = (uint)largeInt; // 2147483647
uint from_neg = (uint) largeNegInt; // 2147483648 or OverflowException is thrown (if checked)
int from_smallFloat = (int) smallFloat; // 17
int from_largeFloat = (int) largeFloat; // -2147483648 or OverflowException is thrown (if checked)
char from_intc = (char) 32; // ' '
char from_invalid_int = (char) 0xdcbf; // invalid char - no exception thrown
// no cast available
int fromString = Int32.Parse("42"); // 42
string toString = largeInt.ToString(); // "2147483647"
int fromString_bad = Int32.Parse("forty two"); // FormatException is thrown
关于 checked 关键字,请参阅这篇文章。
任何类型都可以隐式转换为其基类或接口。
IList<int> ll = new List<int>();
可以使用强制转换运算符把基类转换为派生类。
IList<int> ll = new List<int>();
List<int> l = (List<int>)ll;
如果强制转换失败,即被转换表达式的类型与目标类型无关,就会抛出 InvalidCastException 的实例。
对于这类类型转换,更常见的做法是使用 is 关键字:
interface IFoo { int Bar(); }
class Foo : IFoo { public int Bar() { return 42; }}
Object r = new Random();
IFoo ifoo = new Foo();
Object o = new Foo();
int result = 1729;
if (ifoo is Foo foo)
{
result = foo.Bar();
}
// result == 42
result = 1729;
if (o is Foo foo2)
{
result = foo2.Bar();
}
// result == 42
result = 1729;
if (r is Foo foo3)
{
result = foo3.Bar();
}
// result == 1729
as 关键字的作用与 is 类似,例如 var foo = ifoo as Foo;。在这个例子中,如果 ifoo 不是 Foo 类型,foo 就会是 null;否则 ifoo 会被赋给它。
类型可以定义自己的自定义显式和隐式强制转换运算符。
BCL 中显式和隐式强制转换的例子,就是 BigInteger 结构体与其他数值类型之间的相互转换。
typeof
如果你需要检测对象的精确类型,那么 is 可能过于宽松了,因为它会把对象转换成一个类或其任意基类。在这种情况下,typeof 和 Object.GetType() 才是解决办法。
object o = new List<int>();
o is ICollection<int> // true
o.GetType() == typeof(ICollection<int>) // false
o is List<int> // true
o.GetType() == typeof(List<int>) // true