C# 函数调用中的值参数、引用参数和输出参数

  • Post author:
  • Post category:其他


笔记来源:C#图解教程(第4版)



值参数


:通过将实参的值复制到形参的方式把数据传递下去(将变量副本传递给该方法)


系统步骤:

  1. 在栈中为形参分配空间
  2. 将实参的的值复制给形参(副本)



引用参数


:形参的参数名将作为实参变量的别名,指向相同的内存位置。


系统步骤:

  1. 不会为形参在栈上分配内存
  2. 形参的参数名将作为实参变量的别名,指向相同的内存位置(在方法中对形参的作的任何改变在方法完成后依然有效(表现在实参上))



输出参数


:用于从方法体内把数据传出到调用代码,它们的行为与引用参数非常相似



注意:


值参数

的实参不一定是变量,它可以是任何能计算成相应数据类型的表达式,以变量用作实参之前,变量必须要赋值(除输出参数),对于引用类型,变量可以被设置为一个实际的引用或者null。例如:


引用参数



使用时必须在方法的声明和调用中都使用ref修饰符

,实参必须是变量(需要确切的内存位置),在做实参前必须要赋值,对于引用类型,变量可以被设置为一个实际的引用或者null。


输出参数



使用时必须在方法的声明和调用中都使用out修饰符

,实参必须是变量(需要确切的内存位置),在方法内部,输出参数在能够被读取之前必须被赋值,这意味着参数的初始值是无关的,而且没有必要在方法调用之前为实参赋值,但在方法内部,未赋值前任何语句想要访问该实参都会报错。需要赋值后才能访问操作。

———————————————————分割线————————————————————–

一、值参数中传递

值类型



引用类型

注:值参数是参数传递的一种

方式

,和值类型引用类型是不同概念,不要混淆

先看代码

class MyClass
{
    public int Val = 20;   //初始化字段为20
}

class Program
{
    static void MyMethod(MyClass f1,int f2)//值参数方式传递一个引用类型和一个值类型
    {
        f1.Val = f1.Val + 5;    //对引用类型进行修改,方便比较
        f2     = f2 + 5;        //对值类型进行修改
        Console.WriteLine("f1.Val:{0}, f2:{1}", f1.Val,f2);//输出显示
    }
       
    static void Main()
    {
        MyClass a1 = new MyClass();
        int a2     = 10;

        MyMethod(a1,a2);//调用方法
        Console.WriteLine("f1.Val:{0}, f2:{1}", a1.Val,a2);//输出显示
    }
}

代码结果:f1.Val: 25,f2:15

f1.Val: 25,f2:10

内存原理:

  1. 在方法被调用前,用作实参的变量a2已经在栈里了
  2. 在方法开始时,系统在栈中为形参分配空间,并从实参复制值。

    1. 因为a1是引用类型的,所以引用被复制,结果实参形参都引用堆中的同一个对象。
    2. 因为a2是值类型的,所以值被复制,产生了一个独立的数据项。
  3. 在方法的结尾,f2和对象f1的字段都被加上了5。

    1. 方法执行后,形参从栈中弹出。
    2. a2,值类型,他的值不受方法行为影响。
    3. a1,引用类型,但它的值被方法的行为改变了。

    二、引用参数中传递

    值类型



    引用类型

class MyClass
{
    public int Val = 20;   //初始化字段为20
}

class Program
{
    static void MyMethod(ref MyClass f1,ref int f2)//加上ref修饰符表示以引用参数方式传递一个引用类型和一个值类型
    {
        f1.Val = f1.Val + 5;    //对引用类型进行修改,方便比较
        f2     = f2 + 5;        //对值类型进行修改
        Console.WriteLine("f1.Val:{0}, f2:{1}", f1.Val,f2);//输出显示
    }
       
    static void Main()
    {
        MyClass a1 = new MyClass();
        int a2     = 10;

        MyMethod(ref a1,ref a2);//调用方法
        Console.WriteLine("f1.Val:{0}, f2:{1}", a1.Val,a2);//输出显示
    }
}

代码结果:f1.Val: 25,f2:15

f1.Val: 25,f2:15


内存原理:

  1. 在方法被调用前,用作实参的变量a1、a2已经在栈里了
  2. 在方法的开始,形参名被设置为实参的别名。变量a1和f1引用相同的内存位置,变量a2和f2引用相同的内存位置
  3. 在方法执行之后,形参的名称已经失效,但是值类型a2的值和引用类型a1所指向的对象的值都被方法内的行为改变了

二、输出参数中传递

值类型



引用类型

class MyClass
{
    public int Val = 20;   //初始化字段为20
}

class Program
{
    static void MyMethod(out MyClass f1,out int f2)//加上out修饰符表示以输出参数方式传递一个引用类型和一个值类型
    {
        f1 = new MyClass(); //创建一个类变量
        f1.Val = 25;        //赋值类字段
        f2     = 15;        //赋值int参数
        
    }
       
    static void Main()
    {
        MyClass a1 = null;
        int a2;

        MyMethod(out a1,out a2);//调用方法
    }
}


内存原理:

  1. 在方法被调用前,用作实参的变量a1、a2已经在栈里了
  2. 在方法的开始,形参名被设置为实参的别名。你可以认为变量a1和f1指向的是相同的内存位置,也可以认为a2和f2指向的是相同的内存位置。a1和a2不在作用域之内,所以不能在MyMthod中访问。                  ​​​​​​​
  3. 在方法执行之后,形参的名称已经失效,但是值类型a2的值和引用类型a1所指向的对象的值都被方法内的行为改变了



版权声明:本文为weixin_46009075原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。