-
类型转换:显式与隐式转换
第2章:C#基本语法与数据类型
2.3 类型转换:显式与隐式转换
在C#中,不同数据类型的变量或值之间经常需要“协作”(如参与运算、赋值给其他类型变量等),这就需要类型转换——将一种数据类型转换为另一种数据类型。根据转换的安全性和是否需要手动干预,类型转换分为隐式转换(系统自动完成,安全无风险)和显式转换(需手动指定,可能丢失数据或溢出)。本节将通过实例详解两种转换的核心逻辑、使用场景及扩展转换方式。2.3.1 隐式转换:系统自动完成的“安全转换”
概念与核心原则
隐式转换(Implicit Conversion) 是指编译器自动进行的类型转换,无需开发者手动干预。其核心原则是**“安全无损失”**:转换后的数据不会丢失信息(如精度、范围),且目标类型的取值范围完全覆盖源类型。常见隐式转换场景
· 数值类型从小范围到大范围:如 int(32位整数)→ double(64位浮点数)、byte(8位)→ int(32位)。· 子类到父类的转换(引用类型):如 Student(子类)→ Person(父类),遵循“里氏替换原则”。
· 装箱转换:值类型→ object 或接口类型(如 int → object,前面章节已提及)。
实例2-5:隐式转换——从int到double的自动转换
需求:定义int类型变量,隐式转换为double类型,观察转换前后的值是否一致,验证“无损失”特性。csharp
| using System; | |
| class ImplicitConversionDemo | |
| { | |
| static void Main() | |
| { | |
| // 1. 定义int类型变量(32位整数,范围-2^31~2^31-1) | |
| int numInt = 100; // 源类型:int,值100 | |
| // 2. 隐式转换:int自动转换为double(无需手动干预) | |
| double numDouble = numInt; // 目标类型:double(64位浮点数,范围更大,精度更高) | |
| // 3. 输出转换前后的值和类型 | |
| Console.WriteLine($"源类型(int)值:{numInt},类型:{numInt.GetType().Name}"); // GetType()获取变量类型 | |
| Console.WriteLine($"目标类型(double)值:{numDouble},类型:{numDouble.GetType().Name}"); | |
| // 4. 验证转换无精度损失 | |
| numInt = 200; // 修改源类型变量的值 | |
| Console.WriteLine($"修改源类型后,目标类型值:{numDouble}"); // 目标类型值不受影响(转换是“复制值”的过程) | |
| } | |
| } |
· int numInt = 100;:声明int类型变量numInt,值为100。int是32位有符号整数,取值范围较小(约±20亿)。
· double numDouble = numInt;:隐式转换发生。由于double(64位浮点数)的取值范围(±1.7×10^308)远大于int,且能精确表示所有int值(因为int的整数范围在double的精确整数范围内),编译器自动将numInt的值(100)复制并转换为double类型(100.0),赋值给numDouble。
· Console.WriteLine(...):通过numInt.GetType().Name获取变量类型名称(输出“Int32”,即int的别名),numDouble.GetType().Name输出“Double”,验证类型已转换。
· numInt = 200;:修改源变量numInt的值,但numDouble的值仍为100.0——因为转换是“值的复制”,而非引用,两者是独立的变量。
2.3.2 显式转换:手动干预的“风险转换”
概念与核心原则
显式转换(Explicit Conversion) 是指需要开发者手动指定转换类型的转换(语法:(目标类型)源变量)。通常用于可能丢失数据或精度的场景,编译器无法确保安全,需开发者确认风险(如double转int会截断小数,long转int可能溢出)。常见显式转换场景
· 数值类型从大范围到小范围:如 double → int(截断小数)、long → int(可能溢出)。· 引用类型的向下转换:如 object → int(拆箱)、Person → Student(需确保对象实际是Student类型,否则抛异常)。
实例2-6:显式转换——从double到int的截断与溢出风险
需求:将double类型变量显式转换为int,观察小数截断现象;将long类型变量显式转换为int,演示溢出风险及checked关键字的作用。csharp
| using System; | |
| class ExplicitConversionDemo | |
| { | |
| static void Main() | |
| { | |
| // 场景1:double转int——截断小数(精度损失) | |
| double pi = 3.14159; // double类型,带小数 | |
| int piInt = (int)pi; // 显式转换:(int)指定目标类型,截断小数部分(非四舍五入) | |
| Console.WriteLine($"double值:{pi} → int值:{piInt}"); // 输出:3.14159 → 3(截断小数) | |
| // 场景2:long转int——溢出风险(范围超出) | |
| long bigNum = 3000000000; // long类型(64位整数,范围-2^63~2^63-1),值30亿(超过int的最大值2147483647) | |
| int smallNum = (int)bigNum; // 显式转换:long转int,可能溢出(默认不检查溢出,结果不可预测) | |
| Console.WriteLine($"未检查溢出:long值{bigNum} → int值{smallNum}"); // 输出:-1294967296(溢出后的值) | |
| // 场景3:使用checked关键字检测溢出(抛出异常) | |
| try | |
| { | |
| int safeNum = checked((int)bigNum); // checked块中,溢出时抛出OverflowException | |
| Console.WriteLine($"检查溢出后的值:{safeNum}"); | |
| } | |
| catch (OverflowException ex) | |
| { | |
| Console.WriteLine($"溢出异常:{ex.Message}"); // 输出:“算术运算导致溢出。” | |
| } | |
| } | |
| } |
· double pi = 3.14159;:声明double类型变量pi,值为3.14159(带小数部分)。
· int piInt = (int)pi;:显式转换语法:(int)是强制转换符,告诉编译器将pi转换为int类型。由于int无法存储小数,编译器直接截断小数部分(3.14159 → 3,而非四舍五入到3),这就是“精度损失”。
· long bigNum = 3000000000;:long是64位整数,30亿在其范围内,但超过了int的最大值(2147483647)。
· int smallNum = (int)bigNum;:显式转换long到int,由于值超出int范围,发生溢出。C#默认不检查数值溢出(性能考虑),此时会返回一个“环绕”后的值(根据二进制补码规则),结果无意义(如30亿→-1294967296)。
· checked((int)bigNum):checked关键字强制编译器检查溢出,若转换后的值超出目标类型范围,立即抛出OverflowException异常,避免程序使用错误值。配合try-catch可捕获异常并处理(如提示用户“数值过大”)。
2.3.3 其他实用转换方式:Parse、TryParse与Convert类
除了基础的隐式/显式转换,C#还提供了多种便捷的类型转换工具,尤其适用于字符串与数值类型的转换(如用户输入的字符串转为数字)。1. Parse方法:字符串→数值类型(可能抛异常)
Parse是数值类型(int、double等)的静态方法,用于将字符串转换为对应数值类型。若字符串格式无效(如“abc”转int),会抛出FormatException;若值超出范围,会抛出OverflowException。实例:
csharp
| string strNum = "123"; | |
| int num = int.Parse(strNum); // 成功:strNum是有效整数格式,返回123 | |
| Console.WriteLine(num); // 输出:123 | |
| string strInvalid = "abc"; | |
| int numInvalid = int.Parse(strInvalid); // 失败:抛出FormatException(“输入字符串的格式不正确”) |
2. TryParse方法:安全转换(不抛异常)
TryParse是更安全的转换方式:尝试转换,成功则返回true并通过out参数输出结果,失败则返回false,结果参数为类型默认值(如int默认0)。推荐用于用户输入场景(避免因输入错误导致程序崩溃)。实例:
csharp
| string userInput = "456"; | |
| bool success = int.TryParse(userInput, out int result); // TryParse返回bool,结果通过out参数传出 | |
| if (success) | |
| { | |
| Console.WriteLine($"转换成功:{result}"); // 输出:456 | |
| } | |
| else | |
| { | |
| Console.WriteLine("转换失败,输入不是有效整数"); | |
| } | |
| string badInput = "xyz"; | |
| int.TryParse(badInput, out int badResult); // 返回false,badResult为0(int默认值) | |
| Console.WriteLine($"失败结果:{badResult}"); // 输出:0 |
3. Convert类:通用类型转换工具
System.Convert类提供了更灵活的转换方法(如Convert.ToInt32、Convert.ToDouble),支持多种源类型(字符串、null、其他数值类型等),且对null有特殊处理(如Convert.ToInt32(null)返回0,而int.Parse(null)抛异常)。实例:
csharp
| // 字符串转int | |
| int num1 = Convert.ToInt32("789"); // 成功:789 | |
| // null转int(返回0) | |
| int num2 = Convert.ToInt32(null); // 0(int默认值) | |
| // bool转int(true→1,false→0) | |
| int num3 = Convert.ToInt32(true); // 1 | |
| // double转int(四舍五入,而非截断) | |
| int num4 = Convert.ToInt32(3.7); // 4(区别于(int)3.7→3) |
2.3.4 知识扩展与延伸
一、隐式转换的“安全”本质
为什么C#只允许部分类型的隐式转换?核心是**“类型安全”**——编译器确保转换不会丢失信息。例如:· int → double:安全(double可精确表示所有int值);
· double → int:不安全(double的小数部分会丢失,大值会溢出),因此必须显式转换。
类型安全检查:编译器通过“隐式转换运算符”判断是否允许自动转换,自定义类型也可通过重载implicit operator实现隐式转换(需确保安全)。
二、显式转换的风险与规避
显式转换可能导致两种问题:· 精度损失:如double→int截断小数(3.9→3);
· 溢出:如long→int值超出范围(30亿→负数)。
规避方案:
· 转换前检查值范围(如if (doubleValue <= int.MaxValue && doubleValue >= int.MinValue));
· 使用checked关键字强制溢出检查;
· 优先使用TryParse(字符串转数值)或Convert类(提供更安全的范围检查)。
三、装箱拆箱与类型转换的关系
前面章节提到的装箱(值类型→引用类型)和拆箱(引用类型→值类型)本质也是类型转换:· 装箱:隐式转换(如int→object);
· 拆箱:显式转换(如(int)obj,需确保obj是装箱的int,否则抛InvalidCastException)。
性能影响:装箱拆箱涉及堆内存分配和复制,频繁操作会降低性能,应尽量使用泛型(如List<int>而非ArrayList)避免。
总结
· 隐式转换:系统自动完成,安全无损失(如int→double),无需手动干预;· 显式转换:需手动指定(目标类型),可能丢失精度或溢出(如double→int),需开发者承担风险;
· 实用工具:Parse(简单转换,抛异常)、TryParse(安全转换,返回bool)、Convert(通用转换,处理null和多类型);
· 核心原则:类型转换的本质是“数据表示形式的转换”,需根据场景选择安全方式,避免数据错误或程序崩溃。
掌握类型转换是数据处理的基础,后续LINQ查询、数据库操作等场景中,字符串与数值的转换、不同数值类型的运算都依赖本节知识。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlePrograme/robot/49339.html
栏目列表
最新更新
循环语句:for、while与foreach
第二十四章:工作流高级逻辑
第二十三章:工作流基础搭建
第二十二章:高级自动化与条件分支
条件语句:if-else与switch
第二十一章:自动化规则入门
第二十章:回收站与文件恢复
第十九章:搜索功能:从文件到内容
第2章:C#基本语法与数据类型
第18章 领域应用案例
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比










