C#学习笔记——基础概念——值传递、引用传递、输出传递

方式描述
值传递值传递会复制参数的实际值并赋值给函数的形式参数,实参和形参使用的是两个不同内存位置中的值,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全
引用传递引用传递会复制参数的内存位置并传递给形式参数,当形参的值发生改变时,同时也会改变实参的值
输出传递输出传递可以一次返回多个值

值传递过于简单,不讲

ref关键字

   ref关键字用于按引用传递参数。这意味着不是传递参数的值,而是传递该参数的内存地址。因此,任何对参数的修改都会影响到原始变量。这与按值传递形成对比,在按值传递中,方法接收到的是参数的一个副本,对副本的修改不会影响原始变量。

📌使用场景

ref关键字主要用于以下几种情况:

  1. 按引用传递参数:允许方法修改调用者提供的参数,并让这些修改反映在调用者的环境中。
  2. 返回引用类型的数据:从C# 7.0开始,ref还可以用于返回一个引用(通过ref return),并可以与局部变量一起使用来存储这些引用(通过ref local)。

📌基本语法

        方法声明时使用ref当定义,如果希望某些参数以引用的方式传递,则需要在参数列表中指定ref关键字。

        调用方法时使用ref在调用带有ref参数的方法时,也需要在实际参数前加上ref关键字,并且传入的参数必须是变量(不能是常量或表达式)。

int myNumber = 5;
ModifyValue(ref myNumber);
Console.WriteLine(myNumber); // 输出: 15

📌示例代码

这里有一个简单的例子展示了如何使用ref关键字:

using System;

class Program
{
    static void Main()
    {
        int a = 1, b = 2;
        Console.WriteLine($"Before swap: a = {a}, b = {b}");
        
        Swap(ref a, ref b);
        
        Console.WriteLine($"After swap: a = {a}, b = {b}");
    }

    static void Swap(ref int x, ref int y)
    {
        int temp = x;
        x = y;
        y = temp;
    }
}

输出结果:

Before swap: a = 1, b = 2
After swap: a = 2, b = 1

📌C# 7.0及之后的ref增强功能

        自C# 7.0以来,ref关键字得到了扩展,支持了ref returns(引用返回)和ref locals(引用本地变量)。这使得函数可以返回对数据的引用而不是其副本,同时也允许创建指向这些引用的本地变量。

ref return示例
using System;

class RefExample
{
    private static int[] array = new int[] { 1, 2, 3, 4 };

    public static ref int Find(int target) // 返回数组中第一个匹配项的引用
    {
        for (int i = 0; i < array.Length; i++)
        {
            if (array[i] == target)
                return ref array[i];
        }
        throw new ArgumentOutOfRangeException(nameof(target), "Target value not found");
    }

    static void Main()
    {
        ref int result = ref Find(3); // 查找数字3
        result = 0; // 修改找到的元素

        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
        // 输出:
        // 1
        // 2
        // 0
        // 4
    }
}

 

out关键字

🧠 一、什么是 out 参数?

在 C# 中,out 是一个关键字,用于将参数按引用传递,但它与 ref 不同:

  • ref:要求变量在传入前必须已经赋值。
  • out:不要求变量在传入前赋值,但必须在方法内部赋值

也就是说,out 主要用于从方法中传出值,而不是传入值再修改它。


🧠 二、基本语法

方法定义时使用 out

public void GetData(out int result)
{
    result = 42; // 必须赋值
}

调用时使用 out

int value;
GetData(out value);
Console.WriteLine(value); // 输出 42

注意:调用时也必须写 out,即使变量已经声明。


🔁 三、为什么需要 out?应用场景有哪些?

场景 1:返回多个值

C# 方法默认只能返回一个值(通过 return)。如果想返回多个值,可以使用 out 参数。

public void GetCoordinates(out int x, out int y)
{
    x = 100;
    y = 200;
}

调用:

int a, b;
GetCoordinates(out a, out b);
Console.WriteLine($"x: {a}, y: {b}"); // 输出 x: 100, y: 200

场景 2:Try 模式(TryParse 等)

这是 .NET 中非常常见的设计模式,用于避免异常处理带来的性能开销。

例如:

string input = "123";
int number;
bool success = int.TryParse(input, out number);

if (success)
{
    Console.WriteLine($"成功解析: {number}");
}
else
{
    Console.WriteLine("输入不是数字");
}

TryParse 返回布尔值表示是否成功,而真正的结果通过 out 参数返回。


场景 3:性能优化(避免频繁创建对象)

比如在游戏开发或高性能场景中,你可以复用对象并通过 out 参数填充数据。

public bool TryGetPlayerStats(int id, out PlayerStats stats)
{
    if (_playerCache.TryGetValue(id, out stats))
    {
        return true;
    }

    // 如果缓存中没有,则从数据库加载
    stats = LoadFromDatabase(id);
    _playerCache[id] = stats;
    return true;
}

⚠️ 四、注意事项 & 常见错误

❌ 错误 1:未在方法内赋值

void BadMethod(out int x)
{
    // 编译错误!out 参数必须赋值
}

✅ 正确做法:

void GoodMethod(out int x)
{
    x = 0; // 必须赋值
}

❌ 错误 2:调用时不写 out

int val;
GetValue(val); // 错误!应该写 out val

✅ 正确写法:

GetValue(out val);

❌ 错误 3:试图传入表达式或常量

GetValue(out 123);     // ❌ 错误
GetValue(out Math.PI); // ❌ 错误

✅ 只能传入变量:

int val;
GetValue(out val); // ✅ 正确

💡 五、C# 7.0+ 的改进:out 变量直接声明

从 C# 7.0 开始,可以在调用时直接声明 out 变量,无需提前声明:

if (int.TryParse("123", out int num))
{
    Console.WriteLine(num);
}

这样写更简洁,也减少了作用域污染。


📦 六、与 ref 的对比总结

特性outref
是否需要初始化否(但必须在方法内赋值)是(必须在调用前赋值)
使用目的从方法传出值传入并可能被修改的值
调用时是否要加关键字
是否可重载是(void Foo(ref int) 和 void Foo(out int) 是不同方法)
是否允许只读字段作为参数✅(如果字段是 readonly 但类型是结构体)

🧩 七、高级用法(C# 7.0+)

ref locals(引用本地变量) + out

虽然不常见,但你可以结合 refout 来操作引用。

Span<int> numbers = stackalloc int[] { 1, 2, 3 };
ref int found = ref FindValue(2, out int index);

Console.WriteLine($"Found at index {index}: {found}");

// 修改原数组中的值
found = 99;

Console.WriteLine(string.Join(", ", numbers)); // 输出: 1, 99, 3

 out 是一种“只出不进”的参数传递方式,适合用于从方法中传出一个或多个值,尤其适用于 Try 模式和性能敏感的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值