方法参数的传递

1.值类型

   a.按值传递

   系统自动把实参的数值赋值给形式参数,即被调用方法接受到的只是实际参数的一个副本,当在方法内不修改参数数据时,不会影响实际参数的值,因为他们是俩个不同的变量,有不同的内存地址

   b.按引用传递

    实际中希望方法返回多个结果,或者需要修改传入参数的值并返回,只用return是无法做到的,这时候就需要ref关键字,在调用方法时,把实际参数的引用赋值给形式参数,实参的引用代表数据的值的内存地址,因此实际参变量和形式参变量将指向同一个内存地址,再修改形式参数后,同时也修改了实际参数

class Program
{
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
 2. 引用类型

     a. 按值传递

   引用类型的变量不直接包含其数据;它包含的是对其数据的引用。当通过值传递引用类型的参数时,有可能更改引用所指向的数据,如某类成员的值。但是无法更改引用本身的值;也就是说,不能使用相同的引用为新类分配内存并使之在块外保持。若要这样做,应使用 ref 或 out 关键字传递参数。将向方法传递指向 对象引用的一个副本。输出显示方法有可能更改对象元素的内容,但是,在 方法内使用 new 运算符来分配新的内存部分,将使变量 参数引用新的对象。因此,这之后的任何更改都不会影响原始对象。实际上,本示例中创建了两个对象

     b.按引用传递

     方法内发生的任何更改都会影响调用程序中的原始变量。使用 new 运算符对原始对象进行了重新分配。



  1. class test  
  2. {  
  3.     public string s = null;  
  4.     public int i = 0;  
  5. }  
  6.   
  7. public void run()  
  8. {  
  9.     test instance = new test();  
  10.     instance.s = "first";  
  11.     instance.i = 1;  
  12.     callByValue(instance);  
  13. }  
  14.   
  15. public void callByValue(test t)  
  16. {  
  17.     t.s = "changed";  
  18.     t.i = 2;  
  19. }  

  广泛会被误认为按引用传递实际上是按 引用传递的参数传递方式...它的执行结果非常明显,参数引用类型test的实例instance的成员s和i一定会被更改,所以看起来它似乎确实是按引用传递的...但是,错的这个参数传递的是该参数实例的一个副本!

引用类型实例的副本是什么呢?就是这个instance的引用的副本...也就是说,这个时候在栈上,原来的instance的引用还在,传递给callByValue方法的参数t是栈上instance的引用的copy...这个copy引用指向托管堆上instance值的地址,所以一改俱改...所以表象似乎一样,但和C/C++传递指针的方式本质是差别巨大的...


我们把callByValue方法稍作修改,如下...

[c-sharp]  view plain  copy
  1. public void callByValue(test t)  
  2. {  
  3.     test tmp=new test();  
  4.     tmp.s = "changed";  
  5.     tmp.i = 2;  
  6.     t = tmp;     
  7. }  

结果很显然,不会变...instance和instance的成员都不会变,原因呢?最常见的解释是作用域不同,tmp只在callByValue方法体中存活,所以呢,出了这个方法体就不起作用了...胡说八道!出了方法体tmp废弃了,那instance应该是null才对啊?!什么思维逻辑...其实很简单,上面说了,这个传递进来的引用只是个副本,修改这个副本不会对在栈上的instance引用有丝毫影响,新构造的实例也跟托管堆上instance的值毫不相干..(指向了新的副本).当退出方法体时,这个副本随即被当作垃圾废弃,instance和它的成员自然不会变...

[c-sharp]  view plain  copy
  1. public void callByReference(ref test t)  
  2. {  
  3.     test tmp=new test();  
  4.     tmp.s = "changed";  
  5.     tmp.i = 2;    
  6.     t = tmp;     

传递进去的是instance的引用本身,自然在t=tmp;时instance的引用被修改指向tmp在托管堆上的值...

这就改变了...tmp被废弃了,但是instance却改变了...因为这时传递进去的是instance的引用本身,自然在t=tmp;时instance的引用被修改指向tmp在托管堆上的值...

 

最后来看看“特殊”的string...在被当做参数传递时,string一点也不特殊...

[c-sharp]  view plain  copy
  1. public void run()  
  2. {  
  3.     string instance = "first";  
  4.     callByValue(instance);  
  5.     callByReference(instance);  
  6. }  
  7.   
  8. public void callByValue(string s)  
  9. {  
  10.     s = "changed";  
  11. }  
  12.   
  13. public void callByReference(ref string s)  
  14. {  
  15.     s = "changed";  
  16. }  

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# 编程指南)

Visual Studio 2005

引用类型的变量不直接包含其数据;它包含的是对其数据的引用。当通过值传递引用类型的参数时,有可能更改引用所指向的数据,如某类成员的值。但是无法更改引用本身的值;也就是说,不能使用相同的引用为新类分配内存并使之在块外保持。若要这样做,应使用 ref 或 out 关键字传递参数。为了简单起见,下面的示例使用 ref

下面的示例演示通过值向 Change 方法传递引用类型的参数 arr。由于该参数是对 arr 的引用,所以有可能更改数组元素的值。但是,试图将参数重新分配到不同的内存位置时,该操作仅在方法内有效,并不影响原始变量 arr

C#
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]);
    }
}

Inside Main, before calling the method, the first element is: 1

Inside the method, the first element is: -3

Inside Main, after calling the method, the first element is: 888

上个示例中,数组 arr 为引用类型,在未使用 ref 参数的情况下传递给方法。在此情况下,将向方法传递指向 arr 的引用的一个副本。输出显示方法有可能更改数组元素的内容,在这种情况下,从 1 改为 888。但是,在 Change 方法内使用 new 运算符来分配新的内存部分,将使变量 pArray 引用新的数组。因此,这之后的任何更改都不会影响原始数组 arr(它是在 Main 内创建的)。实际上,本示例中创建了两个数组,一个在 Main 内,一个在 Change 方法内。

本示例除在方法头和调用中使用 ref 关键字以外,其余与上个示例相同。方法内发生的任何更改都会影响调用程序中的原始变量。

C#
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]);
    }
}

Inside Main, before calling the method, the first element is: 1

Inside the method, the first element is: -3

Inside Main, after calling the method, the first element is: -3

方法内发生的所有更改都影响 Main 中的原始数组。实际上,使用 new 运算符对原始数组进行了重新分配。因此,调用 Change 方法后,对 arr 的任何引用都将指向 Change 方法中创建的五个元素的数组。

交换字符串是通过引用传递引用类型参数的很好的示例本示例中,str1 和 str2 两个字符串在 Main 中初始化,并作为由 ref 关键字修改的参数传递给 SwapStrings 方法。这两个字符串在该方法内以及 Main 内均进行交换。

C#
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);
    }
}

Inside Main, before swapping: John Smith

Inside the method: Smith John

Inside Main, after swapping: Smith John

传递值类型参数(C# 编程指南)

Visual Studio 2005

类型变量直接包含其数据,这与引用类型变量不同,后者包含对其数据的引用。因此,向方法传递值类型变量意味着向方法传递变量的一个副本。方法内发生的对参数的更改对该变量中存储的原始数据无任何影响。如果希望所调用的方法更改参数的值,必须使用 ref 或 out 关键字通过引用传递该参数。为了简单起见,下面的示例使用 ref

下面的示例演示通过值传递值类型参数。通过值将变量 n 传递给方法 SquareIt。方法内发生的任何更改对变量的原始值无任何影响。

C#
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);
    }
}

The value before calling the method: 5

The value inside the method: 25

The value after calling the method: 5

变量 n 为值类型,包含其数据(值为 5)。当调用 SquareIt 时,n 的内容被复制到参数 x 中,在方法内将该参数求平方。但在 Main 中,n 的值在调用 SquareIt 方法前后是相同的。实际上,方法内发生的更改只影响局部变量 x

下面的示例除使用 ref 关键字传递参数以外,其余与上一示例相同。参数的值在调用方法后发生更改。

C#
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);
    }
}

The value before calling the method: 5

The value inside the method: 25

The value after calling the method: 25

本示例中,传递的不是 n 的值,而是对 n 的引用。参数 x 不是 int 类型,它是对 int 的引用(本例中为对 n 的引用)。因此,当在方法内对 x 求平方时,实际被求平方的是 x 所引用的项:n

更改所传递参数的值的常见示例是 Swap 方法,在该方法中传递 x 和 y 两个变量,然后使方法交换它们的内容。必须通过引用向 Swap 方法传递参数;否则,方法内所处理的将是参数的本地副本。以下是使用引用参数的 Swap 方法的示例:

C#
static void SwapByRef(ref int x, ref int y)
{
    int temp = x;
    x = y;
    y = temp;
}

调用该方法时,请在调用中使用 ref 关键字,如下所示:

C#
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);
}

i = 2 j = 3

i = 3 j = 2

 总之,值传递时:都传递副本,但是值类型一定不改变原数据,引用类型只要不分配新的内存就改变。

          引用类传递是: 都改变

https://msdn.microsoft.com/zh-cn/library/9t0za5es(v=vs.80).aspx




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值