博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
值类型和引用类型
阅读量:7123 次
发布时间:2019-06-28

本文共 6187 字,大约阅读时间需要 20 分钟。

一,值类型特性

1.C#的所有值类型均隐式派生自System.ValueType。 

2.每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。例如:int i = new int();等价于:int i = 0;

3.所有的值类型都是密封(seal)的,所以无法派生出新的值类型。

4.值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。

二,引用类型的特性:

1.C#的所有引用类型均隐式派生自System.object。

2.引用类型可以派生出新的类型。

3.引用类型可以包含null值。

4.引用类型变量的赋值只复制对对象的引用,而不复制对象本身。

5.引用类型的对象总是在进程堆中分配(动态分配)。

解释下内存下的堆和栈的区别(堆栈是两种数据结构):

1、栈:由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;

2、堆:一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

 PS:

1,所有继承System.Value的类型都是值类型,其他类型都是引用类型。

System.Enuml类继承自System.Value但是它是引用类型

2,C#枚举类型都是值类型,System.Enum不是枚举类型,

3,System.Enum是一个抽象类(abstract class),所有枚举类型都直接继承自它,当然也同时继承了它的所有成员。那么System.Enum属于引用类型

4,枚举类型继承自一个引用类型后,却还是值类型!所有的值类型都是System.ValueType的后代,枚举类型也不例外,枚举类型直接继承自System.Enum,而System.Enum却又直接继承自System.ValueType的,所以,枚举类型也是System.ValueType的后代。

5,正确的说法应该是“值类型都是System.ValueType的后代”,但System.ValueType的后代不全是值类型,System.Enum就是唯一的特例!在System.ValueType的所有后代中,除了System.Enum之外其它都是值类型。

三,引用类型和值类型的例子:

 PS:将一个值类型的值赋值给另一个值类型:int a = 1; int b = a;这里的意思是将a的值拷贝给b,而如果定义一个还没初始化的引用类型时:A a = new A(); A a1; a1 = a;这里的意思a1和a同时指向于同一块内存,所以若a的值改变会影响a1的值

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace value{    class Program    {        static void Main(string[] args)        {            //引用类型输出            //理解一:当两个引用类型(t,t1)同时引用同一个对象时,更改这个对象的值(A)时,两个引用类型同时变化            Test t = new Test(100, 200);            Test t1 = t;            t.A = 200;            Console.WriteLine("{0},{1}", t.A, t1.A);  //200 200            //理解二:当两个引用类型(t3,t4)先同时引用同一个对象时,而当给t3引用一个新的对象,而t4依旧引用旧的对象,并没有受到t3的影响            //则此时更改t3的A值不会影响到t4的A的值            Test t3 = new Test(100, 200);            Test t4 = t3;            t3 = new Test(300, 600);            t3.A = 200;            Console.WriteLine("{0},{1}", t3.A, t4.A);  //200 100            //值类型输出,理解两个结构体的值互补影响            MyStruct s = new MyStruct(100, 200);            MyStruct s1 = s;            s.A = 200;            Console.WriteLine("{0},{1}", s.A, s1.A); //200 100            Console.ReadKey();        }        //引用类型        public class Test        {            public int A;            public int B;            public Test(int a, int b)            {                this.A = a;                this.B = b;            }        }        //值类型        public struct MyStruct        {            public int A;            public int B;            public MyStruct(int a, int b)            {                this.A = a;                this.B = b;            }        }    }}

 

 

以上输出:

200,200 

200,100

 综上:

1,Test类,我们Test t =new Test();这样实例t对象,则已经在内存分配了块空间,然而如果是这样Test t1;这样定义,是还没有在内存为其分配空间

2,然而Test t1 = t;在这里有指的是将t1指向t所分配的内存空间,所以t和t1是两个对象的数据是一样的,则会出现上面程序调试的出一致的结果

3,那Test t =new Test();又怎么理解?

理解:

1》如果仅仅写Test t:表示创建了一个对象但是没有分配内存空间,实际上没有初始化

2》而Test t =new Test();表示创建了一个对象,并且调用这个类的构造函数初始化对象,Test()这个是构造函数,用来做初始化。

四,理解完值类型和引用类型,同时理解下装箱和拆箱

装箱:将值类型装换为引用类型

拆箱:将引用类型装换为值类型

然而在理解装箱和拆箱时会有下面几个容易理解错的知识点,上代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace ObjectType{    class Program    {        static void Main(string[] args)        {            //一,当将值类型赋值给object,这里是拆箱和装箱            object a = "a的初始值";            object b = a;            //不提示无法将类型Int隐式装换为string,这里是值类型装换为引用类型,即是拆箱和装箱的理解,不是用引用类型来理解            //即是使用变量a和b都引用了("a的初始值")这个装箱的结果            //而当a = 1;时的意思是:使用变量a引用了("a的修改值")这个装箱的结果,而b依旧是使用("a的初始值")这个装箱的结果没变化            a = "a的修改值";            Console.WriteLine(b);       //输出值为:a的初始值            //Console.WriteLine(a.GetType());            //Console.WriteLine(b.GetType());            //二,当将引用类型赋值给object,并不属于拆箱装箱            object a1 = new UsingType("a1的初始值"); //object 是内置引用类型之一。            object b1 = a1;            //重新赋值的意思是:a1引用了新的对象,但是因为b1变量还引用旧的对象,则旧的对象没有被GC销毁,所以b1的值不改变,而a1引用新的对象,值出现变化            a1 = new UsingType("a1的修改值");            Console.WriteLine(b1);    //输出值为:a的初始值            //三,var 和 object 的区别            //理解var是有类型的,就例如,当你定义了var a,机器判定了a是int型的,就会以int型的方式保存数据,和你直接定义 int 是一样的。            var c = "c的初始值";            object c1 = "c1的初始值";             //c = 1;     //无法将类型Int隐式装换为string            c1 = 1; //这里不提示错误            var c2 = new UsingType("c2的初始值");            object c3 = new UsingType("c3的初始值");            Console.WriteLine(c);            Console.WriteLine(c2.Str);            //Console.WriteLine(c3.Str);   //这里的c3是用不了UsingType类中的Str属性            Console.Read();        }    }    public class UsingType {        public UsingType(string str) {            this.Str = str;        }        public override string ToString()        {            return this.Str.ToString();        }        public string Str { get; set; }    }}

 

五,拆箱装箱性能消耗的原因: 

1》在类型系统中,任何值类型和引用类型都可以和object类型进行转化,装箱转化 是指将一个值类型显式或者隐式的转化为一个object类型,或者是转化成一个被该值类型应用的接口类型,

2》将一个值类型装箱,就创建了一个object实 例,并且将这个值赋值给了object,object对象的数据位于堆中,在栈上有对该对象的引用,而被装箱的类型的值是被作为一个复制的文件赋给对象 的

3》所谓拆箱,就是装箱操作的反操作,复制堆中的对象至栈中,并且返回其值。

六,那堆和栈又怎么理解?

1》“栈”其实就是一种后入先出(LIFO)的数据结构。在我们.NET Framework里面,由CLR负责管理,我们程序员不用去担心垃圾回收的问题;每一个线程都有自己的专属的“栈”。

2》“堆”的存放就要零散一些,并且由 Garbage Collector(GC)执行管理,我们关注的垃圾回收部分,就是在“堆”上的垃圾回收;其次就是整个进程共用一个“堆”。

3》如果是引用类型里面存在存在类型的话,那这个引用类型的值类型该存堆还是栈?

结果是:引用类型中的值类型存在栈中,但是“栈”上有指向这个引用类型中的值类型变量的指针,所以引用类型中的值类型被使用完同时会自动回收

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace DuiZhan{    class Program    {        static void Main(string[] args)        {            //实际上引用类型的中的值类型也是存在栈中,并且由一个位于“栈”上的指针引用,即是栈中存的值类型会被引用,在函数执行完毕后,“栈”同样会被清空。            //而引用类型就需要被GC回收            //例子一 引用同一对象的引用类型的值类型            //当引用类型中存在值类型是,值类型的值存在堆还是栈呢?            TestInt ti = new TestInt();            ti.Val = 1;            TestInt ti2 = ti;            ti2.Val = 2;            Console.WriteLine(ti.Val);  //输出2            //例子二 引用不同对象的引用类型的值类型            //因为重新初始化,重新分配内存空间,即使将ti值付给ti3,在更改ti3的值,却不会影响ti的值            //原因ti和ti3并不是引用同一对象,            TestInt ti3 = new TestInt();             ti3 = ti;            ti3.Val = 3;            Console.WriteLine(ti.Val); //输出3        }    }    //引用类型    public class TestInt    {        public int Val;    }}

六,在程序运行中解析

1,任何一个操作的响应都在一个线程里面,每个线程都有自己的操作内存,叫线程栈,

2,线程栈随着线程的执行完毕,值都要被释放的,
3,值类型是存在线程栈里面的,而引用类型变量在栈里,可是引用类型的值存在堆里,就是变量的地址指向堆

转载于:https://www.cnblogs.com/May-day/p/6431301.html

你可能感兴趣的文章
聊聊架构
查看>>
小米4.0系统如何不Root激活xposed框架的方法
查看>>
Android跨界面共享数据——LiveData应用
查看>>
华为余承东:自产AI芯片 旗舰机比苹果、三星强
查看>>
微软Azure SQL数据仓储供优惠价格购买预留容量
查看>>
Java8ConcurrentHashMap
查看>>
数据分析Power BI数据可视化教程(三)——如何创建矩阵和表以及散点图
查看>>
NoSQL最新现状和趋势:云NoSQL数据库将成重要增长引擎
查看>>
Vue组件传值
查看>>
react-native搭建用例(非CRNA)
查看>>
HTTP协议
查看>>
github简单使用
查看>>
Python提取网站数据笔记
查看>>
隐私政策
查看>>
一些个人认为值得推荐的IT编程技术社区、博客或文章收集与分享
查看>>
排序算法性能比较
查看>>
Java设计模式-策略模式
查看>>
java B2B2C 源码 多级分销springmvc mybatis多租户电子商城系统-注册中心Eureka
查看>>
学习笔记(4.6)
查看>>
java B2B2C Springcloud多租户电子商城系统-spring-cloud-eureka
查看>>