C#值类型和引用类型
资料:《Learning hard C#学习笔记》李志
值类型:
1.包含类型:
简单类型:int,long,short,sbyte,uint,ulong,ushort,byte,char,float,double,decimal,bool
枚举类型:enum
结构体类型:struct
引用类型:
1.包含类型:
类类型:string,Console类和自定义类
数组类型:一维数组,多维数组
接口类型:interface
委托类型:delegate
值类型和引用类型的区别:
1.值类型分配在线程堆栈上(管理由操作系统负责),引用类型分配在托管堆上(管理由垃圾回收器GC负责)。管理指内存空间的分配和释放
1)变量本身是存储在堆栈上的(无论是值类型变量还是引用类型变量)
2)但是对于实际数据:引用类型存在托管堆上,值类型存在堆栈
2.值类型继承自valueType,valueType继承自System.Object;引用类型直接继承自System.Object
3.值类型在作用域内结束时,会被操作系统自释放,减少托管堆压力;引用类型则靠GC。因此值类型在性能上由优势
4.值类型是密封的,不能作为基类。引用类型一般具有继承性
5.值类型不能为null,默认初始化为该类型的默认值;引用类型默认初始化为null
6.值类型作为参数传递时,不影响本身。引用类型作为参数传递时,会改变最终该变量的值
装箱:值类型转为引用类型
步骤:
1)内存分配:在托管堆分配内存空间
2)实际数据的复制:将值类型的数据复制到新分配的内存中
3)地址返回
拆箱:引用类型转为值类型
步骤:
1)检查实例:引用类型是否为Null?不为Null那和拆箱后的类型是否是同一类型
2)地址返回
3)数据复制:堆数据复制到栈
装箱和拆箱都会对数据进行复制,所以会影响性能。最好使用泛型编程
在方法的形参操作中,值类型调用方法后不会更改原数据的值,引用类型则会更改数据的值。但是引用类型的string是具有不可变性的,例如在形参传递string类型的数据,该方式通过重新分配一个新的空间存储新数据实现的。
无论值类型还是引用类型,都可以通过ref,out关键字实现引用传递。这样就解决了返回多个值问题
public IActionResult Index()
{
ValueTypeAndRefType newType = new ValueTypeAndRefType();
int age = 6;
newType.changeByValue(ref age);//age:100
string name = "zs";
newType.changeByRef(ref name);
ViewBag.Cat66 = name;//name:abc 实际上因为string不可变,也是开辟了新的空间,只是把name的指向改成了新的变量指向
return View();
}
public class ValueTypeAndRefType //类是引用类型,通过调用类,再去改里面的值,那么该实例化类的该值也会被改掉
{
public int valueType = 3;
public int method()
{
int age = 10;//堆栈
isValueType(age);
return age; //10
}
public void isValueType(int val)
{
val = 20;
}
public void changeByValue(ref int val)
{
val = 100;
}
public void changeByRef(ref string val)
{
val = "abc";
}
}
ref和out的区别:
ref的参数必须在方法外初始化,out的参数在方法内必须赋值
重载是不看ref和out的,背后的编译是一样的
is和as类型转换:(转换类型最好通过is,as判断,因为强制转换会抛出异常)
is用来判断类型是否可以转换(引用类型转换,装箱,拆箱)
as是两个引用之间进行转换,成功则赋值,不成功则返回null
//is,as
object o = new object();
if (o is int)//引用类型转换,装箱,拆箱
{
ViewBag.Cat66 = "yes";
}
else
{
ViewBag.Cat66 = "no";
}
string n = null;
object m = n as object;//两个引用之间进行转换
if (m == null)
{
ViewBag.Cat66 = "no";
}
else
{
ViewBag.Cat66 = m;
}