C# 中的类型转换详解

C# 中的类型转换详解

想全面了解 C# 中各种类型转换的概念、分类和使用方式,包括隐式 / 显式转换、装箱拆箱、自定义转换以及 is/as 运算符等核心内容,我会从基础到进阶逐一讲解,让你清晰理解每种转换的本质和使用场景。

一、什么是转换?

在 C# 中,转换 是指将一个数据类型的值转换为另一个数据类型的值的过程。C# 是强类型语言,编译器会严格检查类型匹配,转换的核心目的是解决 "不同类型之间的赋值、运算、传参" 问题,同时保证类型安全。

比如:把 int 类型的 10 转成 double 类型的 10.0,把 object 类型的变量转回 string 类型,这些都是转换。

二、转换的核心分类

C# 中的转换主要分为两大维度:

  1. 按 "是否需要手动指定" 分:隐式转换显式转换(强制转换)
  2. 按 "转换对象类型" 分:数值转换引用转换装箱 / 拆箱转换用户自定义转换

1. 隐式转换(Implicit Conversion)

定义 :无需手动指定,编译器自动完成的安全转换,不会丢失数据或引发异常。核心特点:从 "范围小 / 精度低" 的类型转到 "范围大 / 精度高" 的类型,或从派生类转到基类(引用类型)。

数值类型的隐式转换示例:
复制代码
// 整数间的隐式转换:int(4字节)→ long(8字节)
int num1 = 100;
long num2 = num1; // 编译器自动转换,无风险

// 数值精度提升:int → double
int num3 = 5;
double num4 = num3; // 结果为 5.0,无精度丢失

// 引用类型隐式转换:派生类 → 基类
class Animal {}
class Dog : Animal {}
Animal animal = new Dog(); // 隐式转换,安全

2. 显式转换(Explicit Conversion)/ 强制转换

定义 :必须手动使用 (目标类型) 语法指定的转换,可能丢失数据、精度,甚至引发异常(需开发者自行承担风险)。核心场景:从 "范围大 / 精度高" 的类型转到 "范围小 / 精度低" 的类型,或基类转派生类。

数值类型的显式转换示例:
复制代码
// double(8字节)→ int(4字节),可能丢失小数部分
double pi = 3.14159;
int piInt = (int)pi; // 结果为 3,小数部分被舍弃

// long → short,可能超出范围(溢出)
long bigNum = 100000;
short smallNum = (short)bigNum; // 结果可能是负数(溢出),需注意
显式转换的安全替代:Convert

强制转换可能静默溢出,推荐使用 System.Convert 类(会抛出明确异常):

复制代码
double invalidNum = 1000000.0;
try
{
    int result = Convert.ToInt32(invalidNum); // 超出int范围,抛出OverflowException
}
catch (OverflowException ex)
{
    Console.WriteLine("转换溢出:" + ex.Message);
}

三、各类核心转换详解

1. 数字的转换

数字转换是最常用的转换,分为:

  • 同类型范围转换 :如 byte → int(隐式)、int → byte(显式)
  • 不同精度转换 :如 int → double(隐式)、double → int(显式)
  • 进制 / 格式转换 :如字符串转数字(int.Parse("123"))、数字转字符串(123.ToString("X")
关键方法:

表格

方法 用途 特点
int.Parse(string) 字符串转 int 非数字字符串抛 FormatException
int.TryParse(string, out int) 安全字符串转 int 失败返回 false,不抛异常
Convert.ToInt32(object) 任意类型转 int 支持 null(返回 0),溢出抛异常
(int)数值 强制转换 静默截断 / 溢出,性能高但不安全

示例:

复制代码
// 安全字符串转数字
string input = "123";
if (int.TryParse(input, out int num))
{
    Console.WriteLine("转换成功:" + num); // 输出 123
}
else
{
    Console.WriteLine("转换失败");
}

// 数字转不同进制字符串
int num = 255;
string hex = num.ToString("X"); // 十六进制:FF
string binary = Convert.ToString(num, 2); // 二进制:11111111

2. 引用转换

引用转换仅针对引用类型(类、接口、委托),转换的是 "引用的指向",而非对象本身,分为:

  • 隐式引用转换 :派生类 → 基类 / 接口(如 Dog → Animalstring → object
  • 显式引用转换 :基类 → 派生类 / 接口(如 Animal → Dog),需确保对象实际类型匹配,否则抛 InvalidCastException

示例:

复制代码
class Animal {}
class Dog : Animal { public void Bark() => Console.WriteLine("汪汪"); }
class Cat : Animal { public void Meow() => Console.WriteLine("喵喵"); }

// 隐式引用转换
Animal animal1 = new Dog(); // 正确:Dog是Animal的子类

// 显式引用转换(成功场景)
if (animal1 is Dog) // 先判断类型
{
    Dog dog = (Dog)animal1; // 转换成功
    dog.Bark(); // 输出 汪汪
}

// 显式引用转换(失败场景)
Animal animal2 = new Cat();
// Dog dog2 = (Dog)animal2; // 抛InvalidCastException,因为animal2实际是Cat

3. 装箱转换(Boxing)

定义 :将值类型 (int、double、struct、枚举)转换为 object 类型或该值类型实现的接口类型。本质:值类型数据被复制到托管堆中,生成一个 "装箱对象",返回该对象的引用。

示例:

复制代码
// 装箱:int(值类型)→ object(引用类型)
int num = 10;
object obj = num; // 装箱操作,num的值被复制到堆上

// 验证:obj是装箱后的int
Console.WriteLine(obj.GetType()); // 输出 System.Int32

4. 拆箱转换(Unboxing)

定义 :将装箱后的 object 类型转换回原值类型 ,是装箱的逆操作。要求

  1. 必须显式转换;
  2. 目标类型必须与装箱前的原始类型完全一致(如装箱的 int 不能拆箱为 long);
  3. 未装箱的 object 拆箱会抛 InvalidCastException

示例:

复制代码
// 先装箱
int num = 10;
object obj = num;

// 拆箱(正确)
int num2 = (int)obj; // 拆箱成功,堆上的值被复制回栈

// 拆箱(错误)
// long num3 = (long)obj; // 抛InvalidCastException,类型不匹配

⚠️ 性能提示:装箱 / 拆箱会产生内存复制和 GC 开销,高频场景(如循环)应避免不必要的装箱拆箱(可使用泛型优化)。

5. 用户自定义转换

当系统内置的转换无法满足需求时(如自定义结构体 / 类之间的转换),可通过 implicit(隐式)或 explicit(显式)关键字自定义转换。

定义规则:
  1. 必须定义在转换的源类型或目标类型中;
  2. 必须是 static 方法;
  3. 隐式转换需保证安全(无异常、无数据丢失),显式转换允许有风险。

示例:自定义温度单位转换(摄氏度 ↔ 华氏度)

复制代码
public struct Celsius
{
    public double Value { get; }
    public Celsius(double value) => Value = value;

    // 隐式转换:Celsius → Fahrenheit(安全,无数据丢失)
    public static implicit operator Fahrenheit(Celsius c)
    {
        return new Fahrenheit(c.Value * 9 / 5 + 32);
    }

    // 显式转换:Fahrenheit → Celsius(可能有精度损失,显式更安全)
    public static explicit operator Celsius(Fahrenheit f)
    {
        return new Celsius((f.Value - 32) * 5 / 9);
    }
}

public struct Fahrenheit
{
    public double Value { get; }
    public Fahrenheit(double value) => Value = value;
}

// 使用自定义转换
Celsius c = new Celsius(25);
Fahrenheit f = c; // 隐式转换:25℃ → 77℉
Console.WriteLine(f.Value); // 输出 77

Celsius c2 = (Celsius)f; // 显式转换:77℉ → 25℃
Console.WriteLine(c2.Value); // 输出 25

6. is 运算符

作用 :检查一个对象的实际类型 是否是指定类型(或派生类型),返回 bool 值(true/false),不会抛出异常。场景:转换前先判断类型,避免显式转换抛异常。

语法:对象 is 目标类型

示例:

复制代码
object obj = "Hello";

// 判断类型
if (obj is string)
{
    string str = (string)obj; // 安全转换
    Console.WriteLine(str.Length); // 输出 5
}

if (obj is int) // false,obj实际是string
{
    Console.WriteLine("是int类型");
}

// C# 7.0+ 增强:判断+赋值
if (obj is string strValue)
{
    Console.WriteLine(strValue.ToUpper()); // 输出 HELLO
}

7. as 运算符

作用 :将对象转换为指定的引用类型或可空值类型,转换失败时返回 null(而非抛异常)。限制

  1. 仅适用于引用转换可空值类型转换
  2. 不能用于值类型(除非是可空值类型,如 int?)。

语法:对象 as 目标类型

示例:

复制代码
object obj1 = new Dog();
object obj2 = new Cat();

// 转换成功
Dog dog = obj1 as Dog;
if (dog != null)
{
    dog.Bark(); // 输出 汪汪
}

// 转换失败(返回null)
Dog dog2 = obj2 as Dog;
if (dog2 == null)
{
    Console.WriteLine("转换失败,obj2不是Dog类型");
}

// 可空值类型转换
object numObj = 10;
int? num = numObj as int?; // 成功,num = 10

object strObj = "test";
int? num2 = strObj as int?; // 失败,num2 = null

四、is vs as 对比

表格

特性 is 运算符 as 运算符
返回值 bool 目标类型实例(成功)/null(失败)
适用类型 所有类型 引用类型、可空值类型
性能 可能两次检查类型(判断 + 转换) 一次检查类型(转换 + 判断 null)
常用场景 仅判断类型,不转换 判断 + 转换,避免异常

总结

核心要点回顾

  1. 转换本质:C# 强类型语言中,将一种类型的值转为另一种类型,核心是平衡 "类型安全" 和 "灵活性"。
  2. 基础转换分类
    • 隐式转换:自动、安全(小→大、派生→基类);
    • 显式转换:手动、有风险(大→小、基类→派生类),推荐用 Convert 替代强制转换。
  3. 特殊转换
    • 装箱:值类型→object(堆复制,有开销);拆箱:object→值类型(需类型匹配);
    • 自定义转换:用 implicit/explicit 实现自定义类型间的转换。
  4. 安全转换工具
    • is:判断类型(返回 bool);
    • as:安全转换(失败返回 null,仅适用于引用 / 可空类型)。

掌握这些转换规则,能让你在编写 C# 代码时避免类型错误,同时兼顾性能和安全性。

相关推荐
游乐码2 小时前
c#冒泡排序
c#·排序算法
玩c#的小杜同学13 小时前
源代码保卫战:给C# 程序(混淆、加壳与反逆向实战)
开发语言·笔记·c#
游乐码18 小时前
c#递归函数
算法·c#
柒儿吖20 小时前
DDlog 高性能异步日志库在 OpenHarmony 的 lycium 适配与分步测试
c++·c#·openharmony
柒儿吖20 小时前
基于 lycium 在 OpenHarmony 上交叉编译 utfcpp 完整实践
c++·c#·harmonyos
柒儿吖21 小时前
基于 lycium 在 OpenHarmony 上交叉编译 komrad36-CRC 完整实践
c++·c#·harmonyos
在路上看风景1 天前
2.1 反射
c#
斯内科1 天前
C#德州扑克梭哈游戏(2):牌型与点数比较
游戏·c#·梭哈
柒儿吖1 天前
rudp Reliable UDP 库在 OpenHarmony 的 lycium 适配与 CRC32 测试
c++·c#·openharmony