当前位置:编程学习 > C#/ASP.NET >>

从赋值操作理解不同类型的函数传参

我们都知道所谓的程序,就是对传入的数据进行操作,最终将处理的数据输出给用户。那么在我们的编程中的体现,就是通过函数(或者方法)参数传入数据,然后通过函数返回值输出处理后的数据。数据的传入涉及到函数的实参和形参的传递,数据的输出涉及到函数的返回值和返回值类型的参数。函数返回值没有什么特别之处,而其他的都是函数传参的问题,既然是传参,那么传参到底传过去了什么?
函数传参与赋值操作

         我们都知道函数传参涉及到形参和实参,那么实参和形参就是两个不同的变量,所以函数传参的过程,类似(或者说就是)使用实参给形参赋值的过程。那么我们先来分析按值传参的情况

         图1是值类型变量的赋值操作图,我们大家都知道值类型变量直接保存变量的内容,所以赋值的过程就是将ValA保存的内容直接拷贝到ValB中,此时两个变量除了保存的值相同外,没有其他的任何关系,所以此时修改ValB的内容并不会引起ValA的变化。这是与值类型按值传参的效果是一样的。

 

图 1.值类型变量赋值

 

         图2展示的是引用类型变量的赋值操作,我们都知道引用类型的变量保存的是一个地址,这个地址指向了变量内容所在的地址,也就是说引用变量保存的是一个地址。那么引用类型的赋值操作同样也是拷贝变量保存的内容,这样ValA和ValB就都指向了同样地址。如果我们修改ValB的成员,那么ValA中也会随之产生变化。我们可以看到值类型和引用类型的赋值操作是一样的,都是对变量直接保存的内容的拷贝过程。但是如果我们使用其他对象对ValB重新赋值,这时ValB就指向了其他对象,ValA还指向原来的对象并没有变化。

 

图 2. 引用类型变量赋值

 

         我们知道C#中引入了ref关键字,使我们可以通过函数参数返回函数的处理结果,对于我们需要同时返回多项结果的时候比较有用。其实这也就是我们通常所说的按引用传参。图3展示了变量地址赋值操作,此时我们使用新的对象对ValB所指定的对象赋值,这是ValA也就指向了新的对象了。C#中的ref是否也是这样操作的呢?我们看以看一下下边的代码和相应的IL就知道了。

 

图 3. 变量地址赋值操作

C#代码

[csharp] 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
 
namespace FunctionParameter 

    class PassByValue 
    {        
        public void PassValueParameterByReference(ref ReferencePara parameter) 
        { 
            parameter.a = 1; 
            parameter = new ReferencePara(); 
        } 
 
        public class ReferencePara 
        { 
            public int a; 
            public int b; 
        } 
    } 

[csharp] 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
 
namespace FunctionParameter 

    class PassByValue 
    {        
        public void PassValueParameterByReference(ref ReferencePara parameter) 
        { 
            parameter.a = 1; 
            parameter = new ReferencePara(); 
        } 
 
        public class ReferencePara 
        { 
            public int a; 
            public int b; 
        } 
    } 

对应的MSIL代码

[csharp] 
//传递的是变量的地址  
.method public hidebysig instance void  PassValueParameterByReference(class FunctionParameter.PassByValue/ReferencePara& parameter) cil managed 

  // 代码大小       17 (0x11)  
  .maxstack  8 
  IL_0000:  nop 
  //加载参数  
  IL_0001:  ldarg.1 
  //加载对象的引用  
  IL_0002:  ldind.ref 
  IL_0003:  ldc.i4.1 
  IL_0004:  stfld      int32 FunctionParameter.PassByValue/ReferencePara::a 
  IL_0009:  ldarg.1 
  IL_000a:  newobj     instance void FunctionParameter.PassByValue/ReferencePara::.ctor() 
  //绑定对象的引用  
  IL_000f:  stind.ref 
  IL_0010:  ret 
} // end of method PassByValue::PassValueParameterByReference 
[csharp] view plaincopy
//传递的是变量的地址 
.method public hidebysig instance void  PassValueParameterByReference(class FunctionParameter.PassByValue/ReferencePara& parameter) cil managed 

  // 代码大小       17 (0x11) 
  .maxstack  8 
  IL_0000:  nop 
  //加载参数 
  IL_0001:  ldarg.1 
  //加载对象的引用 
  IL_0002:  ldind.ref 
  IL_0003:  ldc.i4.1 
  IL_0004:  stfld      int32 FunctionParameter.PassByValue/ReferencePara::a 
  IL_0009:  ldarg.1 
  IL_000a:  newobj     instance void FunctionParameter.PassByValue/ReferencePara::.ctor() 
  //绑定对象的引用 
  IL_000f:  stind.ref    www.zzzyk.com
  IL_0010:  ret 
} // end of method PassByValue::PassValueParameterByReference 

 

 

 

总结

         从我们大学一开始接触计算机编程开始,我们就开始记忆两种函数传参的异同,其实传参的过程就是实参对形参赋值的过程,只不过有时拷贝的是变量的内容,有时拷贝的是变量的地址罢了。
作者:xuhongwei0411

补充:软件开发 , C# ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,