Skip to content

05-数据类型转化

在 C# 编程中,不同数据类型之间的转换是很常见的操作。理解各种转换机制有助于防止类型不匹配引发的错误,提高代码的安全性和可维护性。


一、隐式转换(Implicit Conversion)

编译器自动完成的类型转换,不会丢失数据。通常是从小范围类型转换为大范围类型。

csharp
int a = 10;
double b = a;  // int → double,隐式安全转换

// 整数之间的隐式转换
byte bVal = 100;
short sVal = bVal;    // byte → short ✅
int iVal = sVal;      // short → int ✅
long lVal = iVal;     // int → long ✅

// 浮点之间的隐式转换
float fVal = 3.14f;
double dVal = fVal;    // float → double ✅

// int 到 float/double 的隐式转换(可能丢失精度但允许)
int big = 123456789;
float f = big;  // 隐式转换,大 int 转 float 可能丢失精度

隐式转换对照表

源类型目标类型
byte / sbyteshort, int, long, float, double, decimal
short / ushortint, long, float, double, decimal
int / uintlong, float, double, decimal
long / ulongfloat, double, decimal
floatdouble
派生类基类
实现了接口的类型对应接口

二、显式转换(Explicit Conversion / 强制转换)

必须使用强制转换符号 (类型)可能丢失数据。通常是从大范围类型转换为小范围类型。

csharp
double pi = 3.14159;
int intPi = (int)pi;  // double → int,小数部分被截断
Console.WriteLine(intPi);  // 3

// 大范围整数转小范围
long large = 123456789L;
int small = (int)large;  // long → int,可能溢出

// 浮点转整数
float f = 3.9f;
int i = (int)f;  // 截断小数,结果是 3(不是四舍五入!)

// 精确四舍五入的方案
int rounded = (int)Math.Round(f);  // 4(四舍五入)

显式转换的注意事项

csharp
// 1. 小数截断不是四舍五入
Console.WriteLine((int)3.9);   // 3(截断)
Console.WriteLine((int)-3.9);  // -3(向零取整)

// 2. 超出范围时可能溢出(默认不检查)
int max = int.MaxValue;  // 2,147,483,647
int overflowed = max + 1;  // 不报错,但结果不对(环绕为 -2147483648)

// 3. 使用 checked 检查溢出
checked
{
    // int result = int.MaxValue + 1;  // ❌ 运行时抛出 OverflowException
}

三、Convert 类转换

System.Convert 类提供了在不同基本类型之间转换的方法,失败时抛出异常。

csharp
// 字符串转数字
string str = "123";
int num = Convert.ToInt32(str);    // "123" → 123
double d = Convert.ToDouble(str);  // "123" → 123.0

// 类型互转
int i = 65;
char c = Convert.ToChar(i);  // 65 → 'A'
string s = Convert.ToString(i);  // 65 → "65"
bool b = Convert.ToBoolean(1);   // 1 → true

// double 转 int(会四舍五入,不是截断)
double pi = 3.14159;
int rounded = Convert.ToInt32(pi);  // 3(四舍五入)
int truncated = (int)pi;            // 3(截断)
// 注意:当小数为 .5 时,Convert 采用"银行家舍入"(四舍六入五成双)
Console.WriteLine(Convert.ToInt32(3.5));  // 4(3.5 四舍五入)
Console.WriteLine(Convert.ToInt32(4.5));  // 4(4.5 被舍去!因为 4 是偶数)

Convert 与强制转换的区别

对比项(类型) 强制转换Convert
失败时编译成功,运行时可能异常抛出 FormatException / InvalidCastException
小数处理截断四舍五入(银行家舍入)
字符串转换不支持直接转数字支持
类型兼容性要求类型相关更灵活
null 处理值类型转 null 会异常返回默认值或异常

四、Parse 和 TryParse

专门用于字符串 → 数值类型的转换。

Parse —— 失败抛异常

csharp
int num = int.Parse("123");         // ✅ 成功
double d = double.Parse("3.14");    // ✅ 成功
DateTime dt = DateTime.Parse("2024-10-01");  // ✅ 成功

// int bad = int.Parse("abc");  // ❌ 抛出 FormatException

TryParse —— 失败返回 false(推荐)

csharp
string input = "123";
if (int.TryParse(input, out int result))
{
    Console.WriteLine($"转换成功:{result}");
}
else
{
    Console.WriteLine("转换失败");
}

// TryParse 的简化模式
if (int.TryParse(Console.ReadLine(), out int age))
{
    Console.WriteLine($"你输入的年龄是:{age}");
}
else
{
    Console.WriteLine("无效的数字");
}

Parse 和 TryParse 支持的类型

方法说明
int.Parse() / int.TryParse()字符串 → int
long.Parse() / long.TryParse()字符串 → long
double.Parse() / double.TryParse()字符串 → double
decimal.Parse() / decimal.TryParse()字符串 → decimal
bool.Parse() / bool.TryParse()字符串 → bool
DateTime.Parse() / DateTime.TryParse()字符串 → DateTime
Enum.Parse<>() / Enum.TryParse<>()字符串 → 枚举

Parse vs TryParse 选择

场景推荐
输入确定合法Parse
用户输入(不确定)TryParse
配置文件解析TryParse
性能要求高TryParse(避免异常开销)

五、is 和 as 运算符

用于引用类型之间的类型检查和转换。

is 运算符——检查类型

csharp
object obj = "Hello";

// 判断类型
bool isString = obj is string;   // True
bool isInt = obj is int;         // False

// C# 7.0+ 模式匹配:检查 + 转换
if (obj is string text)
{
    Console.WriteLine($"字符串长度:{text.Length}");
}

// 还可以检查并取反
if (obj is not int)
{
    Console.WriteLine("不是 int 类型");
}

// switch 模式匹配
string Describe(object obj) => obj switch
{
    int i => $"整数:{i}",
    string s => $"字符串:{s}",
    null => "null",
    _ => $"其他:{obj.GetType().Name}"
};

as 运算符——安全转换

csharp
object obj = "Hello";

// as 转换:成功返回目标类型,失败返回 null
string? str = obj as string;   // "Hello"
int? num = obj as int?;        // null(int 是值类型,需用 int?)

if (str != null)
{
    Console.WriteLine(str.Length);  // 安全使用
}

// as 只能用于引用类型和可空值类型
// int i = obj as int;  // ❌ 编译错误:int 不能为 null

is / as / 强制转换对比

方式失败行为适用类型推荐场景
is 检查返回 false所有类型只需要判断类型
as 转换返回 null引用类型 + 可空值类型安全的类型转换
(T)obj 强制转换抛出异常所有类型确定类型一定正确时

六、装箱和拆箱(特殊转换)

装箱(Boxing)将值类型转换为 object 或接口类型,拆箱(Unboxing)将装箱后的对象还原为原始值类型。两者都有显著性能开销。

详细内容已移至独立文档:06-装箱拆箱

csharp
int value = 42;
object boxed = value;       // 装箱:栈 → 堆
int unboxed = (int)boxed;   // 拆箱:堆 → 栈

七、checked 和 unchecked(溢出检查)

csharp
// unchecked:允许溢出(默认)
int x = int.MaxValue;
int y = unchecked(x + 1);  // 结果:-2147483648(绕回)

// checked:溢出时抛异常
try
{
    int z = checked(x + 1);  // 抛出 OverflowException
}
catch (OverflowException)
{
    Console.WriteLine("溢出!");
}

// 在项目设置中默认启用 checked
// 或在代码块级别启用
checked
{
    int result = x + 1;  // 溢出时抛异常
}

八、类型转换方法选择指南

需求场景推荐方法示例
int → long(扩大)隐式转换long l = i;
double → int(缩小)强制转换int i = (int)d;
string → intint.Parse() / TryParse()int.Parse("123")
object → stringas 或强制转换obj as string
任意类型 → string.ToString()age.ToString()
数值 → 其他数值ConvertConvert.ToInt32(d)
枚举 ↔ int强制转换(int)Season.Summer
检查类型is 运算符obj is string

核心知识点总结

转换方式速查

转换方式关键字/方法是否安全是否丢失数据
隐式转换自动安全否(小→大)
显式转换(类型)可能异常可能
Convert 类Convert.ToXxx()可能异常可能
Parsexxx.Parse()可能异常否(格式问题)
TryParsexxx.TryParse()安全(返回 bool)
as 转换obj as T安全(返回 null)
is 检查obj is T安全不转换

注意事项

  1. 强制转换截断小数——(int)3.9 结果是 3,不是 4
  2. Convert 会四舍五入——Convert.ToInt32(3.9) 结果是 4
  3. TryParse 比 Parse 安全——用户输入优先用 TryParse
  4. is + 模式匹配是最优雅的类型检查方式
  5. as 失败返回 null——不会抛异常,比强制转换安全
  6. checked 防止静默溢出——需要时使用 checked 关键字
  7. 装箱拆箱影响性能——大量循环中应避免,使用泛型集合
  8. 拆箱类型必须精确匹配——box 了什么类型,unbox 也要什么类型

Released under the MIT License.