一、什么是转换
- 将一个类型转换为另外一个类型;
- 可以是两个值类型之间的转换;
- 可以是两个引用类型之间的转换;
- 可以是值类型和引用类型之间的转换(拆箱与装箱);
- 可以用户自定义转换。
- 转换的时候有隐式转换/自动转换和显示转换/强制转换。
二、值类型之间的转换
1.隐式转换/自动转换
- 范围小的值类型 转 范围大的值类型,不丢失数据精度。
- 隐式转换最高位用符号位(二进制补码)进行扩展,保证数值的正负是正确的。
- 正数符号位是0,使用0进行扩展的叫做零扩展。
- 负数符号位是1,使用1进行扩展的叫做符号扩展。
值类型之间的隐式转换关系如下图:
2.显示转换/强制转换
- 范围大的值类型 转 范围小的值类型,无法保证精度不丢失。
- 需要使用强转表达式完成转换。
显示转换/强制转换理解示例:
- ushort范围:0~65535
- byte范围:0~255
- 只要希望转换的ushort值小于256,就不会损失数据,否则最高位的数据将丢失(溢出)。
3.值类型之间的转换关系
现在我们应该已经知道,占据较少位的值类型可以隐式转换为占据较多位的值类型。
三、溢出检测上下文
在类型转换时,进行检测结果是否溢出的代码片段称为溢出检测上下文。
- 如果我们指定一个表达式或一段代码为checked,CLR会在转换产生溢出时抛出一个异常。
- 默认溢出检测上下文是不检查,如果不检查,转换将继续而不管是否产生溢出。
1.checked和unchecked运算符
2.checked语句和unchecked语句
checked语句和unchecked语句可以嵌套在任意层次。
四、引用类型之间的转换
引用类型转换接受源引用并返回一个指向同一位置的引用,但是把引用“标记”为其他类型。
1.隐式转换/自动转换
- 类可以隐式转换到它继承链中任何类以及它实现的任何接口。
- 所有引用类型可以被隐式转换到object类型。
- 任何类型可以隐式转换到它继承的接口。
委托可以隐式转换为.NET BCL类和接口。
ArrayS数组,其中的元素都是Ts类型,可以隐式转换:
a. .NET BCL类和接口
b. 另一个数组ArrayT,其中元素都是Tt类型(当满足下面所有条件可转换)
i. 两个数组有一样的维度
ii. 元素Ts和元素Tt都是引用类型,而不是值类型
iii. 在元素Ts和Tt之间存在隐式转换
2.显示转换/强制转换
- 从object到任何引用类型的转换
- 从基类到它继承的类的转换
-
无效强制转换
如果将基类强制转换成派生类时,派生类并没有创建对象,就将一个基类对象直接强转为派生类对象,会报InvalidCastException异常(无效强制转换异常)。
-
有效强制转换
第一种情况:派生类转基类,本身是隐式转换,可以但没必要的强制转换。
第二种情况:源引用为null
第三种情况:派生类先转基类,再基类转派生类。(先向上转型,再向下转型)
五、值类型和引用类型之间的转换
1.装箱
- 含义: 值类型隐式转换成引用类型
- 装箱产生一个原始值类型和引用类型副本,每一个都可以独立进行操作。
- 任何值类型ValueTypeS都可以被隐式转换为object类型、System.ValueType、或InterfaceT(如果ValueTypeS实现了InterfaceT)
2.拆箱
- 含义:把装箱后的对象转换成值类型的过程。
- 拆箱是显示转换。
- 尝试将一个值拆箱为非原始类型,会抛出一个InvalidCastException异常。
六、用户自定义转换
除了标准转换,我们还可以为类和结构定义隐式或显示转换。
- 需要public和static修饰
- 除了implicit和explicit关键字之外,隐式和显示转换的声明语法是一样的。
- 语法格式如下:
用户自定义转换的约束:
- 只可以为类和结构定义用户自定义转换。
- 不能重定义标准隐式转换或强制转换。
- 对于相同的源类型S和目标类型T,不能声明隐式转换或强制转换。
- 对于源类型S和目标类型T,以下命题为真:
i.S和T必须是不同类型
ii.S和T不能通过继承关联
iii.S和T都不能是接口类型或object类型
iiii.转换运算符必须是S或T的成员
1.单步用户自定义转换
隐式转换代码示例:
internal class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
//将Person隐式转换为int
public static implicit operator int(Person person)
{
return person.Age;
}
//将int类型转换成Person类型
public static implicit operator Person(int i)
{
return new Person("NONE", i);
}
}
internal class Program
{
static void Main(string[] args)
{
Person bill = new Person("Bill", 25);
int age = bill;//将bill隐式转换为int类型
Console.WriteLine("Person Info:{0},{1}",bill.Name,age);
Person person = 17;//将int类型隐式转换为Person类型
Console.WriteLine("Person Info:{0},{1}", person.Name, person.Age);
}
}
显示转换代码示例:
internal class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
//将Person显式转换为int
public static explicit operator int(Person person)
{
return person.Age;
}
//将int类型转换成Person类型
public static explicit operator Person(int i)
{
return new Person("NONE", i);
}
}
internal class Program
{
static void Main(string[] args)
{
Person bill = new Person("Bill", 25);
int age = (int)bill;//将bill显式转换为int类型
Console.WriteLine("Person Info:{0},{1}",bill.Name,age);
Person person =(Person) 17;//将int类型显式转换为Person类型
Console.WriteLine("Person Info:{0},{1}", person.Name, person.Age);
}
}
2.多步用户自定义转换
在用户自定义转换时,可能存在多种转换组合。
代码示例:
class Employee : Person { }
class Person
{
public string Name;
public int Age;
//将Person对象转换为int
public static implicit operator int(Person person)
{
return person.Age;
}
}
internal class Program
{
static void Main(string[] args)
{
Employee bill = new Employee();
bill.Name = "Wakaka";
bill.Age = 17;
//1.普通转换: Employee --> Person
//2.用户自定义转换: Person --> int
//3.普通转换: int --> float
float fVar = bill;
Console.WriteLine("Person Info: {0},{1}",bill.Name,fVar);
}
}
代码图解:
七、is和as运算符
1. is运算符
我们可以使用is运算符来检查转换是否会成功,避免盲目转换。
- 语法:Expr is TargetType;
- is运算符只能用于引用转换,以及拆箱、装箱转换,不能用于用户自定义转换。
class Employee : Person { }
class Person
{
public string Name = "光头强";
public int Age = 25;
}
internal class Program
{
static void Main(string[] args)
{
Employee employee = new Employee();
Person person;
if (employee is Person) //检测employee是否能转换为Person类型
{
person = employee;
Console.WriteLine("Person Info: {0},{1}", person.Name,person.Age);
}
}
}
2. as运算符
as运算符和强制运算符类似,但如果转换失败,返回null,而不是抛异常。
- 语法:Expr as TargetType;
- TargetType是目标类型,必须是引用类型
- as运算符只能用于引用转换和装箱转换,不能用于用户自定义转换或到值类型的转换。
class Employee : Person { }
class Person
{
public string Name = "光头强";
public int Age = 25;
}
internal class Program
{
static void Main(string[] args)
{
Employee employee = new Employee();
Person person;
person = employee as Person;
if (person != null)
{
person = employee;
Console.WriteLine("Person Info: {0},{1}", person.Name,person.Age);
}
}
}
(注:本章学习总结自《C#图解教程》)