out为了输出一个新的值,因此参数必须在方法返回值之前明确赋值;
ref为了改变已有的值,因此参数必须在方法调用之前明确赋值。
一、值类型和引用类型
C# 中的类型一共分为两类,一类是值类型(Value Type),一类是引用类型(Reference Type)。
值类型包括结构体(struct)和枚举(enum)。
引用类型包括类(class)、接口(interface)、委托(delegate)、数组(array)等。
常见的简单类型如short、int、long、float、double、byte、char等其本质上都是结构体,对应struct System.Int16、System.Int32、System.Int64、System.Single、System.Double、Syetem.Byte、System.Char,因此它们都是值类型。但string和object例外,它们本质上是类,对应class System.String和System.Object,所以它们是引用类型。
1.值类型
值类型变量本身保存了该类型的全部数据,当声明一个值类型的变量时,该变量会被分配到栈(Stack)上。
2. 引用类型
引用类型变量本身保存的是位于堆(Heap)上的该类型的实例的内存地址,并不包含数据。当声明一个引用类型变量时,该变量会被分配到栈上。如果仅仅只是声明这样一个变量,由于在堆上还没有创建该类型的实例,因此,变量值为null,意思是不指向任何类型实例(堆上的对象)。对于变量的类型声明,用于限制此变量可以保存的类型。
二、值传递和引用传递
C#中方法的参数传递默认的是值传递,引用传递和输出传递需要在参数类型前面对应加上ref、out限制符。
值传递参数(就是方法体内部)是原变量(就是方法体外部)的拷贝副本,值传递参数和原变量的内存地址不同,因此方法中对值传递参数的修改不会改变原变量。
引用传递参数是原变量的指针,引用传递参数和原变量的内存地址相同,相应的方法中对引用传递参数的修改会改变原变量。
1.当方法的参数为传值参数时,即参数前没有ref和out关键词。
a.若参数为值类型,进入方法体的参数实际上是外部变量的一个副本,对值参数的操作永远不会影响变量的值。
b.若参数为引用类型,进入方法体的参数实际上是外部变量的一个副本,不过参数和变量存储的都是一个地址,且是同一个地址。换句话说,他们引用的是同一个对象,因此,在方法体内部对参数做的改变会直接影响到变量(实际工作中,基本上不会这样去干)。另一种情况,在方法体内部,对该参数进行重新的地址赋值,比如parameter=new Class(),进行这个操作,实际上是把一个新的类型实例的地址赋值给了我们的参数。在这种情况下,对参数的修改不会影响外部的变量。(实际工作中,基本上也不会这样去干)
2.当方法的参数为引用参数时,即参数前有reft关键词。引用形参并不创建新的存储位置。引用形参表示的存储位置,恰是在方法调用中作为实参给出的那个变量所代表的存储位置。
总结一下,方法体外的变量是否变化了,分为五种情况:
1.如果参数是值类型,且无ref,则变量不变;
2.如果参数是值类型,且有ref,则变量改变;
3.如果参数是引用类型,且无ref,且在方法体内,对参数进行了新对象赋值,而不是属性更改,则变量不变。
4.如果参数是引用类型,且无ref,且在方法体内,对参数进行了属性更改,则变量改变。
5.如果参数是引用类型,且有ref,则不管方法体内对参数做了什么操作,新对象赋值或者属性更改,变量都会变。