目录
本篇文章来分享一下C#的强制类型转换和安全类型转换,强制类型转换也称为显示转换。在C#中,类型转换是将一种数据类型的值转换为另一种数据类型的操作,核心分为强制类型转换(显式转换)和安全类型转换(as关键字、is关键字)两类。
强制类型转换(显式转换)
强制类型转换是开发者已确认两种类型可兼容的转换方式。它直接对值进行类型转换,若转换不合法会立即抛出异常。
1.核心特性
(1)语法:目标类型 变量名 = (目标类型)源值;
(2)转换逻辑:直接操作内存中数据的类型标识(或值的格式),要求两种类型在编译期/运行期具备“可转换关系”(如继承、值类型兼容)。
(3)错误处理:若转换不合法(如将string转int、将父类实例转子类),立即抛出InvalidCastException异常,中断程序执行(未捕获时)。
(4)适用范围:支持值类型(如int→long)和引用类型(如object→string、子类→父类)。
2. 适用场景
强制转换并非“任意类型都能转”,仅支持以下合法场景:
2.1.值类型之间的兼容转换
当两种值类型具备“范围包含”或“格式兼容”关系时,可强制转换,可能丢失精度
(1)范围窄→范围宽(隐式转换也支持,强制转换可显式声明):
如 int(4 字节)→long(8 字节)、float→double,转换安全无精度丢失。
int num1 = 100;
long num2 = (long)num1;//合法,也可省略 (long) ,隐式转换
(2)范围宽→范围窄(必须强制转换,可能丢失精度):
如long→int、double→float,若源值超出目标类型范围,会导致“溢出”(默认不抛异常,仅返回错误值;开启溢出检查后抛OverflowException)。
long bigNum = 2147483648;//超出 int 最大值(2^31 - 1=2147483647)
int smallNum = (int)bigNum;//溢出,smallNum 结果为2147483648 - 2^32 = -2147483648,错误值
2.2.引用类型的“继承链转换”
父类实例→子类实例,必须强制转换,且运行期需确保父类实例本质是子类对象,父类引用可指向子类对象(多态),但需强制转换才能调用子类特有成员。
public class Animal { }
public class Dog : Animal
{
public void Bark()
{
Debug.Log("小狗汪汪~");
}
}
public class Test : MonoBehaviour
{
public void Start()
{
Animal animal = new Dog();//多态:父类引用指向子类对象
Dog dog = (Dog)animal;//合法:强制转换后可调用 Bark()
dog.Bark();//执行成功
}
}
若父类实例不是子类对象,强制转换会抛 InvalidCastException:
Animal animal = new Animal();//父类实例
Dog dog = (Dog)animal;//运行期抛异常:无法将 Animal 转换为 Dog
2.3.object与其他类型的转换
object是所有类型的基类,其他类型与object之间可强制转换。
其他类型与object之间的转换包括装箱和拆箱:
1.装箱:值类型→object,支持隐式转换,也可强制转换。
2.拆箱:object→值类型,必须强制转换,且object中存储的必须是目标值类型(否则抛 InvalidCastException)。
//装箱:int→object
int num = 10;
object obj = (object)num;//合法,也可省略 (object) 隐式装箱
//拆箱:object→int(必须强制转换)
int num2 = (int)obj;//合法,obj 中存储的是 int
//错误拆箱:obj 中存储的是 int,转 string 抛异常
string str = (string)obj;//运行期抛异常:无法将 int 转换为 string
安全类型转换
安全类型转换是开发者不确定类型是否兼容,希望避免异常的转换方式,依赖as关键字(直接转换)和is关键字(先判断再转换)。
1.as关键字:尝试转换,失败返回null
1.1.核心特性
(1)语法:目标类型 变量名 = 源值 as 目标类型;
(2)转换逻辑:尝试将源值转换为目标类型,若兼容则返回转换后的值;若不兼容则返回null(不抛异常)。
(3)关键限制:仅支持引用类型(如class、interface、string)和可空值类型(如int?、double?);不支持非可空值类型(如 int、float),若用于非可空值类型会编译报错。
(4)错误处理:转换失败时返回null,需通过if(变量名 == null) 判断结果。
1.2.适用场景
1.2.1.引用类型的安全转换
替代 “可能失败的强制转换”,避免异常:
public class Animal { }
public class Dog : Animal
{
public void Bark()
{
Debug.Log("小狗汪汪~");
}
}
public class Cat : Animal
{
public void Meow()
{
Debug.Log("小猫喵喵~");
}
}
public class Test : MonoBehaviour
{
public void Start()
{
Animal animal = new Dog();//父类引用指向 Dog 实例
//用 as 转换为 Dog(兼容,返回 Dog 实例)
Dog dog = animal as Dog;
if (dog != null)
{
dog.Bark();//执行成功
}
//用 as 转换为 Cat(不兼容,返回 null)
Cat cat = animal as Cat;
if (cat != null)
{
cat.Meow();//条件不满足,不执行
}
}
}
1.2.2.可空值类型的安全转换
非可空值类型需先包装为可空值类型,才能用 as 转换:
object obj = 10;//obj中存储的是 int
//错误:非可空值类型 int 不能用 as 转换(编译报错)
//int num = obj as int;
//正确:用可空值类型 int? 转换
int? num = obj as int?;
if (num != null)
{
Console.WriteLine(num); //输出:10
}
//转换失败:obj 中是 int,转 double? 返回 null
double? dNum = obj as double?;
if (dNum == null)
{
Console.WriteLine("转换失败");//输出:转换失败
}
2. is 关键字:先判断兼容性,再转换
is 关键字本身不执行转换,仅判断源值是否兼容于目标类型,返回bool值(true/false)。通常与模式匹配结合,实现“判断 + 转换”一步完成,比as更直观。
2.1.核心特性
(1)语法
基础判断:bool 兼容 = 源值 is 目标类型;
模式匹配(判断 + 转换):if (源值 is 目标类型 变量名)
(2)转换逻辑:先判断源值是否兼容于目标类型(运行期检查);若兼容,自动将源值转换为目标类型并赋值给变量(无需额外强制转换);若不兼容,不执行后续逻辑(不抛异常)。
(3)适用范围:支持所有类型(值类型、引用类型、可空值类型),无as的类型限制。
2.2.适用场景
2.2.1.值类型的安全转换
as不支持非可空值类型,is可完美解决:
object obj = 100;
//用 is 判断并转换为 int(非可空值类型)
if (obj is int num)
{
Console.WriteLine(num + 50);//输出:150(转换成功,直接使用num)
}
//用 is 判断并转换为 double(不兼容,不执行)
if (obj is double dNum)
{
Console.WriteLine(dNum);//不执行
}
2.2.2.引用类型的安全转换(替代as)
相比as,is模式匹配无需手动判断null,代码更简洁:
Animal animal = new Cat();
//用 is 模式匹配:判断是否为 Cat,若是则自动转换为 cat 变量
if (animal is Cat cat)
{
cat.Meow();//执行成功
}
//用 is 模式匹配:判断是否为 Dog,若不是则不执行
if (animal is Dog dog)
{
dog.Bark();//不执行
}
2.2.3.复杂类型的多分支判断
在多类型分支场景中,is模式匹配比as更易读:
public class Animal { }
public class Dog : Animal
{
public void Bark()
{
Debug.Log("小狗汪汪~");
}
}
public class Cat : Animal
{
public void Meow()
{
Debug.Log("小猫喵喵~");
}
}
public class Bird : Animal
{
public void Sing()
{
Debug.Log("小鸟吱吱~");
}
}
public class Test : MonoBehaviour
{
public void Start()
{
AnimalSound(new Dog());//Bark 执行
AnimalSound(new Bird());//Sing 执行
}
private void AnimalSound(Animal animal)
{
if (animal is Dog dog)
{
dog.Bark();
}
else if (animal is Cat cat)
{
cat.Meow();
}
else if (animal is Bird bird)
{
bird.Sing();
}
else
{
Console.WriteLine("未知动物");
}
}
}
强制转换 vs 安全转换
维度 |
强制类型转换 (T)obj |
安全类型转换(as/is) |
语法 |
(目标类型)源值 |
as:源值 as 目标类型 is:源值 is 目标类型 变量 |
错误处理 |
不兼容时抛InvalidCastException |
as:返回 null is:不执行逻辑(均不抛异常) |
支持类型 |
所有类型(值类型、引用类型) |
as:引用类型/可空值类型; is:所有类型 |
适用场景 |
确定类型兼容,需即时暴露错误 |
不确定类型兼容,需避免异常 |
性能 |
无额外开销(直接转换) |
as:无额外开销 is:仅多一次类型判断(可忽略) |
代码健壮性 |
低(异常可能中断程序) |
高(容错性强,错误可显式处理) |
什么时候用哪种转换?
1.优先用is模式匹配
场景:不确定类型兼容、需要多分支判断、转换值类型时;
理由:代码简洁、无异常风险、支持所有类型。
2.其次用as关键字
场景:仅需单次引用类型转换,且需用null判断结果时;
理由:比is少一次类型判断(理论性能略优),但需手动处理null。
3.最后用强制转换 (T)
场景:100%确定类型兼容(如单例模式中反射获取的实例、父类引用明确指向子类对象);
理由:无额外开销,且能即时暴露错误(若代码逻辑出错,强制转换抛异常便于调试);
禁忌:避免在可能不兼容的场景中使用(如用户输入、动态数据转换),否则易引发未捕获异常。
常见错误与避坑
1.非可空值类型用 as 转换:编译报错,需改用 is 或强制转换。
//错误:int 是非可空值类型,不能用 as
int num = obj as int;
//正确:用 is 模式匹配
if (obj is int num) { }
2.强制转换父类实例为子类:若父类实例不是子类对象,运行期抛异常,需先判断(用is)。
Animal animal = new Animal();
//错误:animal 不是 Dog 实例,强制转换抛异常
Dog dog = (Dog)animal;
//正确:先判断再转换
if (animal is Dog dog) { }
3.as转换后不判断null:直接使用可能抛NullReferenceException,需先检查null。
Dog dog = animal as Dog;
//错误:若 dog 是 null,调用 Bark() 抛空引用异常
dog.Bark();
//正确:先判断 null
if (dog != null)
{
dog.Bark();
}
好了,本次的分享到这里就结束啦,希望对你有所帮助~