C#详解类型基础
2017-12-24 11:20
633 查看
首先本文将会说到的知识点:
1.值类型和引用类型的原理
2.线程栈和托管堆
3.装箱和拆箱
4.类型判等
Type)别如byte啊int等,在System.Int32结构类型中定义属性和方法在Int32类型中都可以调用,比如:int a= 1; int b = new int();。原因是因为所有的值类型隐式继承了System.ValueType,它是一个类....上面不是说值类型的基础类型是个结构吗,在c#代码中是看不到这种继承关系的,这个关系只能通过MSIL代码才可以看到,所以说是隐式继承的,才会有引用类型的操作。
这里注意的是,栈(Stack)是一种先进后出的数据结构,堆(Heap)是用于引用类型分配空间的区域,创造一个对象将该对象的地址传给栈上的变量
值类型:枚举、结构(数值类型、bool、用户自定义结构类型)
引用类型:关键字定义类型(类、接口、泛型、事件、委托)、内置引用类型(字符串、对象、动态类型Dynamic)
那么类型在内存中如何表示和展现的呢?下面来看几个例子
值类型:
引用类型:
从上面的简单代码和形象图中可以得知,值类型直接存在栈里面,而引用类型的地址存在栈里面,值存在堆里面,简单的来说new一个对象存在堆里面。要值得注意的是类部类,new一个新的对象,在该对象的里面初始化类部类,该类部类的对象是也是存在堆里面的,而它的引用地址的值是给到对象的变量。
知道值类型和引用类型在内存分布情况后我们接着来讲讲大神们津津乐道的线程栈、托管堆
线程栈、托管堆:
每个正在运行的程序都对应着一个进程(Process),在一个进程内部,可以有一个或多个线程(Thread),每个线程都拥有一块存储数据、参数、局部变量、传进来的数据等,这个就是线程栈。创建一个引用类型的时候,引用变量也利用栈,但这时栈包含的只是对另一个内存位置的引用地址,这个地址指向堆的一块区域,这个区域就是托管堆(跟托管代码不是一个概念,毫无关系)。等程序使用完或者是不再调用这个托管堆里面的数据时,GC就会自动回收空间,当然,c#也提供了手动回收机制,这个可以到后面讲讲c#的内存回收机制。
下面来看看例子:
装箱和拆箱:
装箱:就是将一个值类型转换成等价的引用类型。
MSIL代码就不贴了,这里说说它的执行过程
1)在堆上为新的对象实例分配一个内存
2)将栈上值类型变量的值复制到堆上的对象中
3)将堆上创建的对象的地址返回给引用类型变量
拆箱:跟装箱相反的操作,将一个已近装箱的引用类型转换为值类型。
过程:
1)获取已装箱对象的地址
2)将值从堆的对象中复制到栈上的值变量中。
这里要注意几个问题,装箱和拆箱的操作都是在堆上进行的,执行速度相对来说比较慢,所以尽量避免无意义的拆箱装箱操作。在同种类型中也存在这拆箱和装箱操作,只不过是隐式的,可以省略,例如:int转double,有些的转换会丢失精度或者内存溢出等情况。
类型判等:
动手写写几个案例试试
从上面代码,可以的出一些简要的结论:
1.引用类型的判等是判断栈上面引用,而不是堆上面的数据,判断两个对象是否相等一般用Equals
2.由此可见引用类型在传递参数的时候是直接把引用传过去的
3.值类型在判断的时候是转成同类型再去判断的,由进度低的转向进度高的
4.因此有些时候int类型和double类型做计算的时候,与我们想象中的结果有些小小的偏差,系统自动把int类型转成了double类型做运算的
至此文章结束,祝大家永远周末无bug
有些不对的地方希望提出来相互交流。谢谢大家!!!
如有转发请注明出处:博主地址:http://blog.csdn.net/mango_love
1.值类型和引用类型的原理
2.线程栈和托管堆
3.装箱和拆箱
4.类型判等
基本类型原理:
c#的基本类型是按照数据在计算机内存是如何被分配来划分,一种是值类型(Value Type)基础类型是结构,使用中内存保存在栈中,一种是引用类型(Reference Type)基础类型是类,使用中内存保存在堆中常用关键字new创造空间。还有一种特殊类型,这里称之为简单类型,string类型是一种简单的引用类型,它不需要new关键字创造可以直接来使用,还有一些特殊的值类型(SimpleType)别如byte啊int等,在System.Int32结构类型中定义属性和方法在Int32类型中都可以调用,比如:int a= 1; int b = new int();。原因是因为所有的值类型隐式继承了System.ValueType,它是一个类....上面不是说值类型的基础类型是个结构吗,在c#代码中是看不到这种继承关系的,这个关系只能通过MSIL代码才可以看到,所以说是隐式继承的,才会有引用类型的操作。
这里注意的是,栈(Stack)是一种先进后出的数据结构,堆(Heap)是用于引用类型分配空间的区域,创造一个对象将该对象的地址传给栈上的变量
值类型:枚举、结构(数值类型、bool、用户自定义结构类型)
引用类型:关键字定义类型(类、接口、泛型、事件、委托)、内置引用类型(字符串、对象、动态类型Dynamic)
那么类型在内存中如何表示和展现的呢?下面来看几个例子
值类型:
class Program { static void Main(string[] args) { int i = 1; ValueStack vals; vals.x = 2; } } public struct ValueStack { public int x; }
引用类型:
class Program { static void Main(string[] args) { People pro = new People { name = "Jinx", age = 18, sex = "女" }; } } public class People { public string name; public int age; public string sex; }
从上面的简单代码和形象图中可以得知,值类型直接存在栈里面,而引用类型的地址存在栈里面,值存在堆里面,简单的来说new一个对象存在堆里面。要值得注意的是类部类,new一个新的对象,在该对象的里面初始化类部类,该类部类的对象是也是存在堆里面的,而它的引用地址的值是给到对象的变量。
知道值类型和引用类型在内存分布情况后我们接着来讲讲大神们津津乐道的线程栈、托管堆
线程栈、托管堆:
每个正在运行的程序都对应着一个进程(Process),在一个进程内部,可以有一个或多个线程(Thread),每个线程都拥有一块存储数据、参数、局部变量、传进来的数据等,这个就是线程栈。创建一个引用类型的时候,引用变量也利用栈,但这时栈包含的只是对另一个内存位置的引用地址,这个地址指向堆的一块区域,这个区域就是托管堆(跟托管代码不是一个概念,毫无关系)。等程序使用完或者是不再调用这个托管堆里面的数据时,GC就会自动回收空间,当然,c#也提供了手动回收机制,这个可以到后面讲讲c#的内存回收机制。
下面来看看例子:
class Program { static void Main(string[] args) { int ages = 0; People pro = new People { name = "Jinx", age = 18, sex = "女" }; ages = pro.age; Console.WriteLine("年龄:{0}", ages); } } public class People { public string name; public int age; public string sex; }
装箱和拆箱:
装箱:就是将一个值类型转换成等价的引用类型。
class Program { static void Main(string[] args) { int i = 4; Object boxed = i; } }
MSIL代码就不贴了,这里说说它的执行过程
1)在堆上为新的对象实例分配一个内存
2)将栈上值类型变量的值复制到堆上的对象中
3)将堆上创建的对象的地址返回给引用类型变量
拆箱:跟装箱相反的操作,将一个已近装箱的引用类型转换为值类型。
class Program { static void Main(string[] args) { int i = 4; Object boxed = i; int j = (int)boxed; } }
过程:
1)获取已装箱对象的地址
2)将值从堆的对象中复制到栈上的值变量中。
这里要注意几个问题,装箱和拆箱的操作都是在堆上进行的,执行速度相对来说比较慢,所以尽量避免无意义的拆箱装箱操作。在同种类型中也存在这拆箱和装箱操作,只不过是隐式的,可以省略,例如:int转double,有些的转换会丢失精度或者内存溢出等情况。
类型判等:
动手写写几个案例试试
class Program { static void Main(string[] args) { People p1 = new People(); People p2 = new People(); People p3 = p1; int i = 3; double d = 3.0; Console.WriteLine("p1==p2?{0},p1==p3?{1}", p1 == p2, p1 == p3);//运行结果:p1==p2?False,p1==p3?True Console.WriteLine("p1Equals(p2)?{0},p1Equals(p3)?{1}", p1.Equals(p2), p1.Equals(p3));//运行结果:p1Equals(p2)?False,p1Equals(p3)?True Console.WriteLine("i==d?{0}", i == d);//运行结果:i==d?True Console.Read(); } }
从上面代码,可以的出一些简要的结论:
1.引用类型的判等是判断栈上面引用,而不是堆上面的数据,判断两个对象是否相等一般用Equals
2.由此可见引用类型在传递参数的时候是直接把引用传过去的
3.值类型在判断的时候是转成同类型再去判断的,由进度低的转向进度高的
4.因此有些时候int类型和double类型做计算的时候,与我们想象中的结果有些小小的偏差,系统自动把int类型转成了double类型做运算的
至此文章结束,祝大家永远周末无bug
有些不对的地方希望提出来相互交流。谢谢大家!!!
如有转发请注明出处:博主地址:http://blog.csdn.net/mango_love
相关文章推荐
- 【c#基础】-值类型和引用类型详解
- C#基础语法:可空类型详解
- C#基础:Dispose()、Close()、Finalize()的区别详解
- C# 类型和成员基础以及常量、字段、属性
- c#基础 之对象和类型
- c#语言基础(2)理解值类型和引用类型
- 【C#进阶系列】04 类型基础
- c#基础--枚举类型
- C# 类型基础
- C#基础-----复杂数据类型
- C#学习笔记(基础知识回顾)之值类型与引用类型转换(装箱和拆箱)
- 【Java语言基础】数据类型详解
- C# 类型基础
- C# 打好基础图文详解
- C#控制台基础 func<int,string> int类型参数与string类型返回值
- C# 基础总结--类型与继承
- C#语言基础----值类型
- C#基础知识之类型转换与运算符
- C#基础-数据类型转换
- C#基础视频教程3.3 常见控件类型和使用方法