引用类型与值类型

摘录:

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

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)     {     }

 

 

posted on 2012-11-17 15:21  Gcam  阅读(150)  评论(0编辑  收藏  举报

导航