7.C# —— 方法返回值、值传递、ref/out/in/params

在 C# 开发中,方法 是封装代码逻辑的核心单元,而方法的返回值参数传递机制是必须掌握的基础核心。本文通过通俗易懂的案例,系统讲解 C# 方法返回值、值传递、引用传递(ref/out/in)以及 params 可变参数

一、方法返回值

方法的返回值决定了方法执行后能向外输出什么数据 ,C# 中通过返回值类型定义,分为无返回值有返回值两种,返回值可以是基本类型、自定义对象等。

1. 无返回值(void)

使用 void 关键字修饰,方法执行后不向外传递数据,只能在方法内部执行逻辑(如打印、运算),无法将结果拿到方法外部使用。

cs 复制代码
// 定义:计算两数之和,仅内部打印,无返回值
public static void Test1(int a, int b)
{
    int sum = a + b;
    Console.WriteLine(sum); // 只能在方法内输出结果
}

// 调用:无法接收返回值
Test1(10, 20); // 直接执行,输出30

2. 有返回值(指定类型)

void 替换为具体数据类型 (int/string/ 自定义对象等),必须配合 return 关键字返回数据,return 后的值类型必须与方法定义的返回值类型一致。

(1)返回基本类型(int)
cs 复制代码
// 定义:计算两数之和,返回int类型结果
public static int Test2(int a, int b)
{
    int sum = a + b;
    return sum; // return 跳出方法,返回结果
}

// 调用:接收返回值,可在外部使用
int s1 = Test2(99, 99);
Console.WriteLine(s1); // 输出198
(2)返回自定义对象

方法可以返回类的实例对象,实现复杂数据的返回。

cs 复制代码
// 自定义学生类
public class Student
{
    public string Name { get; set; }
}

// 定义:返回Student对象
public static Student Test3(string name)
{
    Student student = new Student(); // 实例化对象
    student.Name = name; // 赋值属性
    return student; // 返回对象
}

// 调用:接收对象,访问对象属性
Student zs = Test3("张三");
Console.WriteLine(zs.Name); // 输出张三

3. 实战:三数取最大值

cs 复制代码
public static int Max(int a, int b, int c)
{
    int temp = a <= b ? b : a; // 先取a、b最大值
    int max = temp <= c ? c : temp; // 再与c比较取最大值
    return max;
}

// 调用
Console.WriteLine(Max(1, 1, 9)); // 输出9

二、方法参数传递:值传递

C# 中默认参数传递方式是值传递 ,值传递的核心是:传递的是数据的副本(拷贝),根据参数类型(值类型 / 引用类型),表现不同。

1. 值类型参数(int/double/bool 等)

传递时,会拷贝实参的值 给形参,方法内部修改形参,不会影响外部实参

cs 复制代码
// 定义:值类型参数
public static void Test1(int a)
{
    a = 20; // 修改的是拷贝的副本
}

// 调用
int a1 = 10;
Test1(a1);
Console.WriteLine(a1); // 输出10,外部值未变

2. 引用类型参数(类 / 对象等)

传递时,拷贝对象的内存地址给形参(形参和实参指向同一个对象),分两种修改场景:

  • 修改对象属性:会影响外部实参(操作的是同一个对象);
  • 重新 new 对象:不会影响外部实参(形参指向新地址,与原对象断开)。
cs 复制代码
// 自定义人类
public class People
{
    public string Name { get; set; }
}

// 定义:引用类型参数
public static void Test2(People p)
{
    // 方式1:修改对象属性 → 影响外部
    p.Name = "李四"; 
    
    // 方式2:重新new对象 → 不影响外部
    // p = new People() { Name = "李四" };
}

// 调用
People p1 = new People() { Name = "张三" };
Test2(p1);
Console.WriteLine(p1.Name); // 方式1输出李四,方式2输出张三

值传递总结

  • 值类型:修改形参 → 外部不变;
  • 引用类型:改属性→外部变,重新 new→外部不变。

三、引用传递:ref 关键字

ref引用传递关键字 ,作用是:让形参和实参指向同一块内存地址 ,方法内部修改形参,一定会影响外部实参

核心规则

  1. 实参和形参都必须加 ref 关键字;
  2. 实参必须先初始化(赋值)才能传递;
  3. 无论值类型 / 引用类型,修改都会同步到外部。

1. ref 修饰值类型

cs 复制代码
public static void Test1(ref int a)
{
    a = 20; // 直接修改原数据
}

// 调用:实参必须初始化
int a1 = 10;
Test1(ref a1);
Console.WriteLine(a1); // 输出20,外部值被修改

2. ref 修饰引用类型

无论改属性 还是重新 new 对象,都会影响外部实参:

cs 复制代码
public static void Test2(ref People a)
{
    // 方式1:改属性 → 外部变
    // a.Name = "武大郎";
    
    // 方式2:重新new → 外部也变
    a = new People() { Name = "武大郎" };
}

// 调用
People p1 = new People() { Name = "烧饼" };
Test2(ref p1);
Console.WriteLine(p1.Name); // 输出武大郎

四、引用传递:out 输出参数

out 也是引用传递关键字 ,核心作用:让方法返回多个结果,同时具备引用传递特性。

核心规则

  1. 实参和形参都必须加 out 关键字;
  2. 实参可以不初始化
  3. 方法内部必须给 out 参数赋值(负责传出数据)。

ref 与 out 区别(重点)

特性 ref out
实参初始化 必须赋值 可以不赋值
方法内赋值 可以不赋值 必须赋值
主要用途 传入 + 传出数据 专门传出多个数据
cs 复制代码
// 定义:out参数实现多返回值
public static void Test1(out int a, out int b)
{
    a = 90; // 方法内必须赋值
    b = 90;
    a = a + b; // 180
    b = (a - b) * b; // 8100
}

// 调用
int a1 = 30; // 可初始化,也可不写
int b1 = 40;
Test1(out a1, out b1);
Console.WriteLine($"a1={a1}, b1={b1}"); // 输出a1=180, b1=8100

五、只读参数:in 关键字

in只读引用传递关键字 ,作用:以高效的引用方式传递参数,且禁止修改形参本身

核心规则

  1. 修饰值类型:禁止修改形参的值;
  2. 修饰引用类型:禁止重新new形参(修改指向),但可以修改对象属性
cs 复制代码
// 1. in修饰值类型:只读,不能修改
public static void Test1(in int a)
{
    // a = 100; ❌ 报错:只读参数无法赋值
    int sum = a + 19;
    Console.WriteLine(sum); // 仅可读
}

// 2. in修饰引用类型:可改属性,不可重新new
public static void Test2(in People a)
{
    a.Name = "foo"; // ✅ 允许修改属性
    // a = new People() { Name = "foo" }; ❌ 报错:禁止修改指向
}

六、可变参数:params 关键字

params 关键字用于定义可变长度参数列表 ,允许传递任意数量的同类型参数,底层会自动封装成数组。

核心规则

  1. params 必须修饰数组 (如 params int[]);
  2. 一个方法只能有一个 params 参数,且必须放在参数列表最后;
  3. 调用时可直接传多个值,无需手动创建数组。
cs 复制代码
// 1. 纯params参数:接收任意数量int
public static void Test1(params int[] a)
{
    // 遍历所有参数
    for (int i = 0; i < a.Length; i++)
    {
        Console.WriteLine(a[i]);
    }
}

// 2. 普通参数+params参数(params放最后)
public static void Test2(int a, int b, params int[] c)
{
    Console.WriteLine(c[0]); // 取params数组第一个值
}

// 调用
Test1(1, 2, 4, 44); // 传递任意数量参数
Test2(10, 20, 30, 40, 50); // a=10,b=20,c=[30,40,50]

七、全流程总结(必背)

  1. 返回值void 无返回,指定类型需 return,可返回基本类型 / 对象;
  2. 值传递(默认):传副本,值类型修改不影响外部,引用类型改属性影响、new 不影响;
  3. ref:引用传递,实参必须初始化,修改同步外部,用于传入 + 传出;
  4. out:引用传递,实参可不初始化,方法内必须赋值,用于多返回值;
  5. in:只读引用,禁止修改形参本身,可改引用类型属性;
  6. params:可变参数,封装为数组,放参数列表最后。

总结

  1. 方法返回值分无返回(void)和有返回(return+指定类型),支持返回对象实现复杂数据输出;
  2. 默认值传递:值类型改不动外部,引用类型改属性有效、new 无效;
  3. ref/out 是引用传递,ref 传值 + 传出,out 专用于多返回值;
  4. in 实现只读引用,params 实现任意数量参数,灵活适配不同业务场景。
相关推荐
雪豹阿伟5 小时前
5.C# —— Math,goto,双for循环
c#·上位机
ゆづき5 小时前
假如编程语言们有外号
java·c语言·c++·python·学习·c#·生活
曹牧15 小时前
C# WinForms应用程序中展示JSON内容
c#
真鬼12317 小时前
.Net 6.0快速下载
c#
雪豹阿伟18 小时前
6.C# —— 类与对象、数据类型、方法详解
c#·上位机
伽蓝_游戏20 小时前
第二章:深入 Unity 资源导入管线 (Asset Import Pipeline)
游戏·unity·c#·游戏引擎·游戏程序
爱炸薯条的小朋友1 天前
全局锁的性能优势,以及链路优化为何常常低于预期——基于 `MatPoolsTest` 中小图池与大图池的实战复盘
opencv·算法·c#
心蓝无敌1 天前
攻克Avalonia Dock布局中WebView等原生控件无法停靠的难题
c#·visual studio·avalonia·avalonia dock
guygg881 天前
C# 监听数据库数据变化(SqlDependency 实现)
数据库·oracle·c#