C#知识学习-018(方法参数传递)

目录

1.按值传递(默认)

1.1 对于值类型(如int, double, struct等)

1.2 对于引用类型(如class)

2. 按引用传递

2.1 ref

2.2 out

2.3 in

2.4 ref readonly

3. params修饰符

4.总结


1.按值传递(默认)

默认情况下,C#中的参数是按值传递的。这意味着方法获得的是变量的副本,而不是原始变量本身。

1.1 对于值类型(如int, double, struct等)

  • 方法得到的是值的副本,所以在方法内修改参数不会影响原始变量。

举例:

void ChangeValue(int x)
{
    x = 10;
}

int number = 5;
ChangeValue(number);
Console.WriteLine(number); // 输出5

1.2 对于引用类型(如class)

引用类型(如类class、数组、字符串等)的变量存储的是对象的引用(地址)。当按值传递引用类型时,传递的是引用的副本(即地址的副本),而不是对象本身的副本。

这意味着:

  • 方法内通过引用副本可以访问和修改原始对象的内容(因为引用副本和原始引用指向同一个对象)。

  • 但是,如果方法内将引用副本指向一个新的对象,那么这不会影响原始引用(因为修改的是引用的副本,而不是原始引用)。

举例:

class Person
{
    public string Name { get; set; }
}

void ChangePerson(Person p)
{
    p.Name = "Jane"; // 影响原始对象
    p = new Person { Name = "John" }; // 不影响原始变量
}

var person = new Person { Name = "Jack" };
ChangePerson(person);
Console.WriteLine(person.Name); // 输出"Jane"

解释一下这个例子:

  • 在调用ChangePerson时,我们将变量person(它包含对原始Person对象的引用)按值传递给方法。注意,对于引用类型,按值传递的是引用的副本。所以,在方法内部,参数p是原始引用的一份拷贝,它指向同一个对象。

  • 步骤1:通过p修改对象的Name属性为"Jane"。因为p和原始变量person指向同一个对象,所以这个修改会影响原始对象。因此,此时原始对象的Name变为"Jane"。

  • 步骤2:将p重新赋值为一个新的Person对象(Name为"John")。注意,这里只是改变了p(它是原始引用的副本)的指向,现在p指向一个新的对象。但是,原始变量person仍然指向原来的对象。所以,这个重新赋值不会影响原始变量。

  • 因此,方法调用后,原始对象的Name是"Jane"。

2. 按引用传递

有时希望方法能够修改原始变量(无论是值类型还是引用类型),这时就需要按引用传递。C#提供了几个修饰符来实现按引用传递:refoutinref readonly

2.1 ref

  • 使用ref关键字,方法获得的是原始变量的引用(即直接操作原始变量)。

  • 调用方法前,变量必须被初始化。

举例:

  • 使用ref按引用传递值类型
void ChangeValueRef(ref int x)
{
    x = 10;
}

int number = 5;
ChangeValueRef(ref number);
Console.WriteLine(number); // 输出10
  • 使用ref按引用传递引用类型
void ChangePersonRef(ref Person p)
{
    p.Name = "Jane"; // 修改对象内容
    p = new Person { Name = "John" }; // 重新赋值,会影响原始变量
}

var person = new Person { Name = "Jack" };
ChangePersonRef(ref person);
Console.WriteLine(person.Name); // 输出"John"

解释:

  • 使用ref关键字按引用传递参数。这意味着我们传递的是原始变量person的引用(即变量本身的地址,而不是引用的副本)。所以,在方法内部,参数p就是原始变量person的别名,对p的任何操作都会直接作用于原始变量。

  • 步骤1:通过p修改对象的Name属性为"Jane"。这同样会修改原始对象,因为p和person指向同一个对象。此时原始对象的Name变为"Jane"。

  • 步骤2:将p重新赋值为一个新的Person对象。由于p是原始变量的别名,所以这个赋值操作实际上改变了原始变量person的指向,现在person指向新的对象(Name为"John")。

  • 因此,方法调用后,原始变量person已经指向了新的对象,所以输出的是新对象的Name:"John"。

2.2 out

  • ref类似,但方法必须在返回前为out参数赋值。

  • 调用方法前,变量可以不初始化

void Initialize(out int x)
{
    x = 100; // 必须赋值
}

int number;
Initialize(out number);
Console.WriteLine(number); // 输出100

2.3 in

  • 按引用传递,但方法不能修改参数(只读)。

  • 调用方法前,变量必须被初始化。

void ReadOnlyIn(in int x)
{
    // x = 10; // 错误,不能修改只读参数
    Console.WriteLine(x);
}

int number = 5;
ReadOnlyIn(in number);

2.4 ref readonly

  • 类似于in,但更明确地表示按引用传递只读参数。

注:方法声明时​​用 ref readonly,​​方法调用时​​用 ref或 in(或者都不用),并且调用时用的修饰符表达了参数的"性质"。

调用时修饰符

含义

适用情况

ref

"我这个变量是可写的"

必须是可写变量

in

"我这个变量可能只读"

可写变量或只读变量

无修饰符

"让编译器决定"

任何表达式(不推荐)

举例:

int writableVar = 10;        // 可写变量
readonly int readOnlyVar = 20; // 只读变量

// 方法声明:必须用 ref readonly
void ProcessData(ref readonly int data)
{
    Console.WriteLine(data);  // 只能读取,不能修改
}

// 用 ref:表示"这个变量是可写的"
ProcessData(ref writableVar);    // 正确:writableVar 确实可写

// 用 ref:错误!
ProcessData(ref readOnlyVar);    // 错误:readOnlyVar 是只读的

// 用 in:表示"这个变量可能是只读的"
ProcessData(in writableVar);     // 正确:可写变量可以用 in
ProcessData(in readOnlyVar);     // 正确:只读变量也可以用 in

// 表达式:不能用 ref 或 in
ProcessData(ref 10);        // 错误!42不是变量
ProcessData(in (x + y));    // 错误!x+y是表达式,不是变量
ProcessData(10);            // 正确!但会有警告

3. params修饰符

params关键字允许方法接受可变数量的参数。它通常用于数组参数。使用 params 修饰符可以简化方法调用,因为你可以直接传递多个参数,而不需要显式地创建数组。

举例:

传统方式(没有params)

// 需要先创建数组
int[] numbers = {1, 2, 3, 4, 5};
CalculateSum(numbers);

void CalculateSum(int[] numbers)
{
    foreach (int num in numbers) {
        Console.WriteLine(num);
    }
}

使用params的方式

// 在参数类型前加 params 关键字
void PrintNumbers(params int[] numbers)
{
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

PrintNumbers(1, 2, 3); // 可以传递多个参数
PrintNumbers(new int[] { 4, 5, 6 }); // 也可以传递数组

注:只能有一个params参数;必须是数组类型;不能与ref、out等修饰符共用

举例:

void Method(string a, params int[] numbers)    //正确
void Method(params int[] numbers1, params string[] numbers2) // 错误

void Method(params int[] numbers)      // 正确:整数数组
void Method(params string[] names)     // 正确:字符串数组
void Method(params int number)         // 错误:不是数组!

void Method(params ref int[] numbers)  // 错误:不能组合使用

4.总结

修饰符

作用

调用前要初始化?

方法内要赋值?

传副本

可选

ref

传引用

可选

out

输出结果

必须

in

只读引用

不能修改

学到了这里,咱俩真棒,记得按时吃饭(祝大家节日快乐~)

【本篇结束,新的知识会不定时补充】

感谢你的阅读!如果内容有帮助,欢迎 ​​点赞❤️ + 收藏⭐ + 关注​​ 支持! 😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值