1.值类型
a.按值传递
系统自动把实参的数值赋值给形式参数,即被调用方法接受到的只是实际参数的一个副本,当在方法内不修改参数数据时,不会影响实际参数的值,因为他们是俩个不同的变量,有不同的内存地址
b.按引用传递
实际中希望方法返回多个结果,或者需要修改传入参数的值并返回,只用return是无法做到的,这时候就需要ref关键字,在调用方法时,把实际参数的引用赋值给形式参数,实参的引用代表数据的值的内存地址,因此实际参变量和形式参变量将指向同一个内存地址,再修改形式参数后,同时也修改了实际参数
class Program2. 引用类型
{
static void Main(string[] args)
{
int a = 0;
Add(a);
Console.WriteLine(a);
Add(ref a);
Console.WriteLine(a);
Console.Read();
}
static void Add(int i)
{
i = i + 10;
Console.WriteLine(i);
}
static void Add(ref int i)
{
i = i + 10;
Console.WriteLine(i);
}
}
结果:10,0,10,10;
a. 按值传递
引用类型的变量不直接包含其数据;它包含的是对其数据的引用。当通过值传递引用类型的参数时,有可能更改引用所指向的数据,如某类成员的值。但是无法更改引用本身的值;也就是说,不能使用相同的引用为新类分配内存并使之在块外保持。若要这样做,应使用 ref 或 out 关键字传递参数。将向方法传递指向 对象引用的一个副本。输出显示方法有可能更改对象元素的内容,但是,在 方法内使用 new 运算符来分配新的内存部分,将使变量 参数引用新的对象。因此,这之后的任何更改都不会影响原始对象。实际上,本示例中创建了两个对象
b.按引用传递
方法内发生的任何更改都会影响调用程序中的原始变量。使用 new 运算符对原始对象进行了重新分配。
广泛会被误认为按引用传递实际上是按 引用传递的参数传递方式...它的执行结果非常明显,参数引用类型test的实例instance的成员s和i一定会被更改,所以看起来它似乎确实是按引用传递的...但是,错的!这个参数传递的是该参数实例的一个副本!
引用类型实例的副本是什么呢?就是这个instance的引用的副本...也就是说,这个时候在栈上,原来的instance的引用还在,传递给callByValue方法的参数t是栈上instance的引用的copy...这个copy引用指向托管堆上instance值的地址,所以一改俱改...所以表象似乎一样,但和C/C++传递指针的方式本质是差别巨大的...
我们把callByValue方法稍作修改,如下...
结果很显然,不会变...instance和instance的成员都不会变,原因呢?最常见的解释是作用域不同,tmp只在callByValue方法体中存活,所以呢,出了这个方法体就不起作用了...胡说八道!出了方法体tmp废弃了,那instance应该是null才对啊?!什么思维逻辑...其实很简单,上面说了,这个传递进来的引用只是个副本,修改这个副本不会对在栈上的instance引用有丝毫影响,新构造的实例也跟托管堆上instance的值毫不相干..(指向了新的副本).当退出方法体时,这个副本随即被当作垃圾废弃,instance和它的成员自然不会变...
传递进去的是instance的引用本身,自然在t=tmp;时instance的引用被修改指向tmp在托管堆上的值...
这就改变了...tmp被废弃了,但是instance却改变了...因为这时传递进去的是instance的引用本身,自然在t=tmp;时instance的引用被修改指向tmp在托管堆上的值...
最后来看看“特殊”的string...在被当做参数传递时,string一点也不特殊...
callByValue方法一定不会更改instance的值,而callByReference方法一定会更改instance的值...原因和上面一样,前者传递的是instance的引用的副本,后者传递的是instance的引用本身...跟什么“不可变性”、“字符串驻留”毫不相干...
///字符串参数的按值与按引用传递 与 值类型一致 (string 是引用类型)
class Program
{
static void Main(string[] args)
{
string a = "Old String";
Add(a);
Console.WriteLine(a);
Add(ref a);
Console.WriteLine(a);
Console.Read();
}
static void Add(string a)
{
a = "new String";
Console.WriteLine(a);
}
static void Add(ref string a)
{
a = "new String";
Console.WriteLine(a);
}
}
传递引用类型参数(C# 编程指南)
引用类型的变量不直接包含其数据;它包含的是对其数据的引用。当通过值传递引用类型的参数时,有可能更改引用所指向的数据,如某类成员的值。但是无法更改引用本身的值;也就是说,不能使用相同的引用为新类分配内存并使之在块外保持。若要这样做,应使用 ref 或 out 关键字传递参数。为了简单起见,下面的示例使用 ref。
下面的示例演示通过值向 Change 方法传递引用类型的参数 arr。由于该参数是对 arr 的引用,所以有可能更改数组元素的值。但是,试图将参数重新分配到不同的内存位置时,该操作仅在方法内有效,并不影响原始变量 arr。
class PassingRefByVal { static void Change(int[] pArray) { pArray[0] = 888; // This change affects the original element. pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local. System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]); } static void Main() { int[] arr = {1, 4, 5}; System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]); Change(arr); System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]); } }
在上个示例中,数组 arr 为引用类型,在未使用 ref 参数的情况下传递给方法。在此情况下,将向方法传递指向 arr 的引用的一个副本。输出显示方法有可能更改数组元素的内容,在这种情况下,从 1 改为 888。但是,在 Change 方法内使用 new 运算符来分配新的内存部分,将使变量 pArray 引用新的数组。因此,这之后的任何更改都不会影响原始数组 arr(它是在 Main 内创建的)。实际上,本示例中创建了两个数组,一个在 Main 内,一个在 Change 方法内。
本示例除在方法头和调用中使用 ref 关键字以外,其余与上个示例相同。方法内发生的任何更改都会影响调用程序中的原始变量。
class PassingRefByRef { static void Change(ref int[] pArray) { // Both of the following changes will affect the original variables: pArray[0] = 888; pArray = new int[5] {-3, -1, -2, -3, -4}; System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]); } static void Main() { int[] arr = {1, 4, 5}; System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]); Change(ref arr); System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]); } }
交换字符串是通过引用传递引用类型参数的很好的示例。本示例中,str1 和 str2 两个字符串在 Main 中初始化,并作为由 ref 关键字修改的参数传递给 SwapStrings 方法。这两个字符串在该方法内以及 Main 内均进行交换。
class SwappingStrings { static void SwapStrings(ref string s1, ref string s2) // The string parameter is passed by reference. // Any changes on parameters will affect the original variables. { string temp = s1; s1 = s2; s2 = temp; System.Console.WriteLine("Inside the method: {0} {1}", s1, s2); } static void Main() { string str1 = "John"; string str2 = "Smith"; System.Console.WriteLine("Inside Main, before swapping: {0} {1}", str1, str2); SwapStrings(ref str1, ref str2); // Passing strings by reference System.Console.WriteLine("Inside Main, after swapping: {0} {1}", str1, str2); } }
传递值类型参数(C# 编程指南)
值类型变量直接包含其数据,这与引用类型变量不同,后者包含对其数据的引用。因此,向方法传递值类型变量意味着向方法传递变量的一个副本。方法内发生的对参数的更改对该变量中存储的原始数据无任何影响。如果希望所调用的方法更改参数的值,必须使用 ref 或 out 关键字通过引用传递该参数。为了简单起见,下面的示例使用 ref。
下面的示例演示通过值传递值类型参数。通过值将变量 n 传递给方法 SquareIt。方法内发生的任何更改对变量的原始值无任何影响。
class PassingValByVal { static void SquareIt(int x) // The parameter x is passed by value. // Changes to x will not affect the original value of x. { x *= x; System.Console.WriteLine("The value inside the method: {0}", x); } static void Main() { int n = 5; System.Console.WriteLine("The value before calling the method: {0}", n); SquareIt(n); // Passing the variable by value. System.Console.WriteLine("The value after calling the method: {0}", n); } }
下面的示例除使用 ref 关键字传递参数以外,其余与上一示例相同。参数的值在调用方法后发生更改。
class PassingValByRef { static void SquareIt(ref int x) // The parameter x is passed by reference. // Changes to x will affect the original value of x. { x *= x; System.Console.WriteLine("The value inside the method: {0}", x); } static void Main() { int n = 5; System.Console.WriteLine("The value before calling the method: {0}", n); SquareIt(ref n); // Passing the variable by reference. System.Console.WriteLine("The value after calling the method: {0}", n); } }
本示例中,传递的不是 n 的值,而是对 n 的引用。参数 x 不是 int 类型,它是对 int 的引用(本例中为对 n 的引用)。因此,当在方法内对 x 求平方时,实际被求平方的是 x 所引用的项:n。
更改所传递参数的值的常见示例是 Swap 方法,在该方法中传递 x 和 y 两个变量,然后使方法交换它们的内容。必须通过引用向 Swap 方法传递参数;否则,方法内所处理的将是参数的本地副本。以下是使用引用参数的 Swap 方法的示例:
static void SwapByRef(ref int x, ref int y) { int temp = x; x = y; y = temp; }
调用该方法时,请在调用中使用 ref 关键字,如下所示:
static void Main() { int i = 2, j = 3; System.Console.WriteLine("i = {0} j = {1}" , i, j); SwapByRef (ref i, ref j); System.Console.WriteLine("i = {0} j = {1}" , i, j); }
总之,值传递时:都传递副本,但是值类型一定不改变原数据,引用类型只要不分配新的内存就改变。
引用类传递是: 都改变
https://msdn.microsoft.com/zh-cn/library/9t0za5es(v=vs.80).aspx