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

本文详细介绍了C#中参数传递的三种方式:值参数、引用参数和输出参数。值参数通过复制实参的值到形参,而引用参数和输出参数则涉及内存地址的传递。引用参数使用`ref`修饰符,允许方法内直接修改实参,输出参数使用`out`修饰符,要求在方法内部初始化并返回结果。示例代码展示了这三种参数类型在传递值类型和引用类型时的内存行为差异。
摘要由CSDN通过智能技术生成

 笔记来源: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所指向的对象的值都被方法内的行为改变了

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值