在 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 是引用传递关键字 ,作用是:让形参和实参指向同一块内存地址 ,方法内部修改形参,一定会影响外部实参。
核心规则
- 实参和形参都必须加
ref关键字; - 实参必须先初始化(赋值)才能传递;
- 无论值类型 / 引用类型,修改都会同步到外部。
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 也是引用传递关键字 ,核心作用:让方法返回多个结果,同时具备引用传递特性。
核心规则
- 实参和形参都必须加
out关键字; - 实参可以不初始化;
- 方法内部必须给 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 是只读引用传递关键字 ,作用:以高效的引用方式传递参数,且禁止修改形参本身。
核心规则
- 修饰值类型:禁止修改形参的值;
- 修饰引用类型:禁止重新
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 关键字用于定义可变长度参数列表 ,允许传递任意数量的同类型参数,底层会自动封装成数组。
核心规则
params必须修饰数组 (如params int[]);- 一个方法只能有一个
params参数,且必须放在参数列表最后; - 调用时可直接传多个值,无需手动创建数组。
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]
七、全流程总结(必背)
- 返回值 :
void无返回,指定类型需return,可返回基本类型 / 对象; - 值传递(默认):传副本,值类型修改不影响外部,引用类型改属性影响、new 不影响;
- ref:引用传递,实参必须初始化,修改同步外部,用于传入 + 传出;
- out:引用传递,实参可不初始化,方法内必须赋值,用于多返回值;
- in:只读引用,禁止修改形参本身,可改引用类型属性;
- params:可变参数,封装为数组,放参数列表最后。
总结
- 方法返回值分无返回(
void)和有返回(return+指定类型),支持返回对象实现复杂数据输出; - 默认值传递:值类型改不动外部,引用类型改属性有效、new 无效;
ref/out是引用传递,ref传值 + 传出,out专用于多返回值;in实现只读引用,params实现任意数量参数,灵活适配不同业务场景。