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;
            }

  

posted @ 2019-07-04 14:22  喵爷66123  阅读(895)  评论(0编辑  收藏  举报