• 引用类型与值类型


    摘录:

    线程堆栈和线程堆:都是内存区域,值类型分配在堆栈上【后进先出】;引用类型变量分配在堆栈上,内容分配在堆上;

    1.值类型

    a.值类型包括结构和枚举,引用类型包括类、接口、委托等。还有一种特殊的值类型,称为简单类型(Simple Type),比如 byte,int等

    b.所有的值类型都隐式地继承自 System.ValueType类型(注意System.ValueType本身是一个抽象类),所以不能显示让一个值类型(结构)再继承另外一个类,因为c#不支持多重继承,而System.ValueType和所有的引用类型都继承自 System.Object基类,

    c.当声明一个值类型的变量(Variable)的时候,变量本身包含了值类型的全部字段,该变量会被分配在线程堆栈(Thread Stack)上。

    d.编译器隐式地会为结构类型创建了无参数构造函数。在这个构造函数中会对结构成员进行初始化,所有的值类型成员被赋予0或相当于0的值(针对Char类型),所有的引用类型被赋予null值。(因此,Struct类型不可以自行声明无参数的构造函数)。所以,我们可以通过隐式声明的构造函数去创建一个ValPoint类型变量:

        public struct ValPoint
        {
            public int x;
            public ValPoint(int x)
            {
                this.x = x;
            }
        }

    ValPoint vPoint1 = new ValPoint(); Console.WriteLine(vPoint.x); // 输出为0   我们将上面代码第一句的表达式由“=”分隔拆成两部分来看:   

    •左边 ValPoint vPoint1,在堆栈上创建一个ValPoint类型的变量vPoint,结构的所有成员均未赋值。在进行new ValPoint()之前,将vPoint压到栈上。   

    •右边new ValPoint(),new 操作符不会分配内存,它仅仅调用ValPoint结构的默认构造函数,根据构造函数去初始化vPoint结构的所有字段。

    注意上面这句,new 操作符不会分配内存,仅仅调用ValPoint结构的默认构造函数去初始化vPoint的所有字段。那如果我这样做,又如何解释呢?  

    Console.WriteLine((new ValPoint()).x);     // 正常,输出为0  

    //在这种情况下,会创建一个临时变量,然后使用结构的默认构造函数对此临时变量进行初始化。

    2.引用類型   

    public class RefPoint
        {
            public int x;
            public RefPoint(int x)
            {
                this.x = x;
            }
            public RefPoint() { }
        }

    当我们仅仅写下一条声明语句:  

    RefPoint rPoint1;   //它的效果是仅仅在堆栈上创建一个不包含任何数据,也不指向任何对象(不包含创建在堆上的对象的地址)的变量。

    而当我们使用new操作符时:  

    rPoint1= new RefPoint(1);

    会发生这样的事:

    1.在应用程序堆(Heap)上创建一个引用类型(Type)的实例(Instance)或者叫对象(Object),并为它分配内存地址。

    2.自动传递该实例的引用给构造函数。(正因为如此,你才可以在构造函数中使用this来访问这个实例。)

    3.调用该类型的构造函数。

    4.返回该实例的引用(内存地址),赋值给rPoint变量。

    /////////////小结值参数、引用类型参数、输出参数///////////////////////////

    1.值参数

    数据通过复制实参的值传给形参,方法被调用时,系统做如下操作:

    A.在栈中为形参分配空间

    B.复制实参的值到形参,这样形参中的值的改变不会影响原来实参中的值,只是原来实参值的一份拷贝。

    ------------实参不一定是变量,它可以是任何能计算成相应数据类型的表达式。

    2.引用参数

    必须在方法地声明和调用中都使用ref修饰符,实参必须是变量,在用作实参前必须被赋值,如果是引用类型变量,可以赋值为一个引用或null值

    A.不在栈中为形参分配新的内存

    B.形参的名称相当于实参变量的别名,跟实参一样引用相同的内存位置。

    3.输出参数

    用于从方法体内把数据传出到调用代码,它们非常类似于引用参数。

    A.必须在声明的调用中都使用out修饰符

    B.实参必须是变量,不能是其他表达式类型

    C.不在栈中为形参分配新的内存

    D.形参的名称相当于实参变量的别名,跟实参一样引用相同的内存位置。

    E.在方法内部,输出参数在被读取之前必须被赋值。

    F.每个输出参数在方法返回之前必须被赋值。

    4.参数数组

    它允许零个或多个实参对应一个特殊的形参。

    A.在一个参数列表中只能有一个参数数组

    B.如果有,它必须是列表中的最后一个

    eg: void ListInts(params int[] inVals)     {     }

  • 相关阅读:
    luogu P1382 楼房
    luogu P1908 逆序对
    5.28 模拟赛
    POJ 2991 Crane
    残(矩阵快速幂)
    AC日记——拍照 洛谷 P3410
    AC日记——[CQOI2014]危桥 洛谷 P3163
    AC日记——【模板】二分图匹配 洛谷 P3386
    AC日记——[ZJOI2009]假期的宿舍 cogs 1333
    AC日记——城市 洛谷 P1401
  • 原文地址:https://www.cnblogs.com/notebook2011/p/2774882.html
Copyright © 2020-2023  润新知