• C#值类型和引用类型


    一,值类型特性

    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,值类型是存在线程栈里面的,而引用类型变量在栈里,可是引用类型的值存在堆里,就是变量的地址指向堆

    七,解析

    1,值类型出现在线程栈:每次调用都有线程栈,,用完自己就结束,变量 - 值类型 都会释放的
    2,引用类型出现在堆里:全局就一个堆,空间有限,所以才需要垃圾回收
    3,操作系统里面,内存是链式分配的,可能有碎片的
    4,CLR的堆:连续分配(数组),空间有限,节约空间
    5,每个应用程序的堆是固定大小的,不会增加

  • 相关阅读:
    HDU 1505 City Game
    HDU 1506 Largest Rectangle in a Histogram
    HDU 1838 Chessboard
    HDU 2512 一卡通大冒险
    HDU 1081 To The Max
    HDU 2845 Beans
    HDU 1422 重温世界杯
    HDU 1231 最大连续子序列
    第6章 接口和实现
    java面向抽象编程样例
  • 原文地址:https://www.cnblogs.com/May-day/p/6431301.html
Copyright © 2020-2023  润新知