NET面向对象基础托管和数据类型
非托管代码和托管代码
非托管代码
软件工程师写的程序,经过编译器转为机器指令后,一般以文件的方式保存在外部存储其中,当CPU执行程序时,要先把外部存储器中的程序指令读到内存。
程序的运行必须依赖于操作系统(如Windows),而且编译生成的程序文件包含的仅适用于特定CPU架构的机器指令,由于不同的CPU架构的机器指令集不同,所以这个可执行程序无法不加修改地拥有不同CPU架构的计算机上。
以这种方式生成的机器指令代码成为“非托管代码(Unmanaged code)”。非托管代码的移植性受到较大的限制。(不同CPU和不相同操作系统)
托管代码
只需为每种操作系统和CPU架构提供一个虚拟机,就可以让同样一个应用程序不加修改的“跑”在运行不同操作系统,拥有不同CPU架构的计算机上。(java使用的中间代码Byte code,.NET使用IL)。
这种运行在虚拟机上的代码,被称为“托管代码(managed code)”(.NET 是CLR)
托管代码调用非托管代码,.NET中称为“平台调用”。托管代码,即时编译只调用一次。CLR会吧编译好的本地代码缓存起来。第二次调用,直接调用缓存中的本地代码,从而避免了再次编译所带来的性能损失。
类和对象
对象是以类为模板而创建出来的实例。在面向对象领域,“对象”和“类的实例”是等同的,它们指代同一事物。“创建对象”有时也称为“类的实例化”。
面向对象的程序运行时,会创建多个对象,这些对象之间可能有着复杂的关联,它们相互协作,共同完成应用程序所提供的各项功能。
在面向对象程序的开发阶段,“类”是核心,而在面向对象程序运行之后,“对象”是核心。
匿名对象 var
当使用var定义隐式类型的局部变量时,必须保证编译器能推断出变量的类型,否则不能通过编译。
var只能用于方法内部定义局部变量,不能定义为类的字段。
使用该匿名类型的对象的好处:隐式类型变量和匿名类型主要用于LINQ中。
值类型和引用类型
值类型value type:使用int和double等数值类型所定义的变量属于值类型。除此之外,“枚举Enum”和“结构体(struct)”也是值类型。
引用类型 Reference Type:类类型,接口类型,数组类型和委托类型
值类型和引用类型变量的内存非配模型是不同的。值类型变量所占用的内存单元是在线程堆栈中分配的。引用类型变量引用的对象所占用的内存是在托管堆中分配的。
线程堆栈和托管堆
每个正在运行的程序都对应一个“进程Process”,在一个进程内部,可以有一个或者多个“线程Thread”,每个线程都拥有一块内存“自留地”,称为“线程堆栈”,大小为1MB,用于保存自身的一些数据,比如线程函数中定义的局部变量、线程函数调用时传送的参数值等,这部分内存区域的分配与回收不需要程序员干涉。
另一块内存区域称为“堆”,在.NET这种托管环境下,所以将其称为“托管堆”。用new关键字创建对象时,分配给对象的内存单元就位于托管堆中。
PS:由于数组对象本身是引用类型,所以不管数组元素时值类型还是引用类型,它所占用的内存都在托管堆上分配。
引用类型变量的相互赋值
两个引用类型变量的相互赋值意味着赋值后,两个引用变量所占用的内存单元其实是相同的。
两个引用类型变相的相互赋值不会导致他们所引用的对象自身被复制,其结果是这两个引用类型的变量引用同一个对象。(一个变化,另一个也会变化。
“==”运算符
== 值类型是比较两个变量的值是否相等
==引用类型是比对两个引用类型变量是否引用同一个对象,而不是比较字段值。
PS:String类型是重写了“==”
装箱和拆箱
通俗的说能够把“值类型”装换成“引用类型”,最简单快捷的方式就是“装箱”。
CLR将值类型的数据“包裹”到一个匿名的托管对象内,并将此托管对象的引用放在引用类型的变量中,这个过程称为“装箱boxing”。把装箱后的数据再赋值给值类型时,要通过“拆箱unboxing”。
装箱和拆箱对程序性能的影响
“装箱”和“拆箱”使我们可以吧值类型变量看成是引用类型的变量,但是这个操作是耗时的,会影响到程序的运行性能,因此应该尽可能的避免在程序中使用装箱和拆箱操作。