C#中参数三兄弟ref、out、in

目录

1. ref (Reference - 引用)

2. out (Output - 输出)

3. in (Input - 输入)

总结对比表


    refoutin 都是参数修饰符,它们都允许通过引用来传递参数,而不是像默认情况那样通过来传递。这意味着方法内部对参数的任何操作都会影响到原始变量。

它们三者之间有着关键的区别,主要体现在数据传递的方向初始化要求上。

1. ref (Reference - 引用)

ref 关键字用于双向数据传递。方法既可以读取传入的值,也可以修改它。

  • 核心思想:有一个已经初始化的变量,想让方法看到它的当前值,并且允许方法修改它。(变量进门要先初始化,方法内外同步修改,像极了一边提需求一边监工的老板。)
  • 规则:
    1. 调用前必须初始化:传递给 ref 参数的变量在调用方法之前必须被赋值。
    2. 方法内可以修改:方法内部可以读取和修改这个变量。
    3. 方法内不是必须修改:方法可以选择不修改它。

示例:
一个经典的例子是交换两个变量的值。

public void RefExample()
{
    int a = 5;
    int b = 10;
    Console.WriteLine($"Before: a = {a}, b = {b}"); // 输出: Before: a = 5, b = 10

    // 必须使用 ref 关键字来调用
    Swap(ref a, ref b);

    Console.WriteLine($"After: a = {a}, b = {b}");  // 输出: After: a = 10, b = 5
}

// 方法定义时也必须使用 ref
void Swap(ref int x, ref int y)
{
    // 方法内可以读取 x 和 y 的值,并修改它们
    int temp = x;
    x = y;
    y = temp;
}

2. out (Output - 输出)

out 关键字用于单向数据传递,方向是从方法内传出到方法外。它通常用于一个方法需要返回多个值的场景。

  • 核心思想:需要方法给我一个值,不需要提前准备好这个值,但必须在方法结束前给我赋一个值。(变量可以空手进门,但方法内必须赋值,否则编译器会拿小本本记仇 。)
  • 规则:
    1. 调用前无需初始化:传递给 out 参数的变量在调用前不需要初始化。
    2. 方法内必须赋值:方法在返回之前必须out 参数赋值。
    3. 方法内不能读取:在为其赋值之前,方法不能读取 out 参数的值。

示例:
最常见的例子是 int.TryParse,它尝试解析一个字符串,如果成功,就通过 out 参数返回结果。

public void OutExample()
{
    string numberString = "123";
    int parsedNumber; // 无需初始化: int parsedNumber = 0;

    //  调用时必须使用 out 关键字
    if (int.TryParse(numberString, out parsedNumber))
    {
        Console.WriteLine($"Parsing successful: {parsedNumber}"); // 输出: Parsing successful: 123
    }
    else
    {
        Console.WriteLine("Parsing failed.");
        // 即使失败,TryParse 内部也会给 parsedNumber 赋值 (通常是 0)
    }

    // parsedNumber 现在是 123
}

// TryParse 的简化版签名
// bool TryParse(string s, out int result) { ... }

3. in (Input - 输入)

in 关键字(C# 7.2 引入)也通过引用传递,但它是只读的。这主要是为了性能优化,特别是当传递大型结构体(struct)时。

  • 核心思想:有一个很大的数据(通常是结构体),想让方法读取它,为了效率不要复制,同时禁止修改这个结构体。(全天候监控的智能摄像头,专门对付那些想偷改数据的「内存刺客」)
  • 规则:
    1. 调用前必须初始化:和 ref 一样,变量在传递前必须被初始化。
    2. 方法内不能修改:方法内部不能修改 in 参数。任何尝试修改它的行为都会导致编译错误。

示例:
有一个非常大的结构体,比如一个三维点,计算它到原点的距离。

public readonly struct Point3D // 使用 readonly struct 是个好习惯
{
    public double X { get; }
    public double Y { get; }
    public double Z { get; }

    public Point3D(double x, double y, double z)
    {
        X = x; Y = y; Z = z;
    }
}

public void InExample()
{
    Point3D myPoint = new Point3D(10, 20, 30); // 必须初始化

    // 调用时可以使用 in,也可以省略(但为了清晰建议写上)
    double distance = CalculateDistance(in myPoint);

    Console.WriteLine($"Distance: {distance}");
}

// 方法定义时使用 in
double CalculateDistance(in Point3D p)
{
    // p.X = 0; // 编译错误! 不能修改 in 参数。

    // 只能读取 p 的值
    return Math.Sqrt(p.X * p.X + p.Y * p.Y + p.Z * p.Z);
}

使用 in 可以避免创建 Point3D 结构体的副本,从而在处理大量或大型结构体时提高性能

总结对比表

特性 (Feature)refoutin
主要目的读/写只写 (输出)只读
数据流向双向 (In/Out)单向 (Out)单向 (In)
调用前必须初始化?
方法内可以修改? (编译错误)
方法内必须赋值?
主要用途修改已存在的值返回多个值性能优化 (避免大结构体复制)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值