ref和out的使用

 
C# 程序员参考 
ref(C# 参考) 

 

ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。例如:

class RefExample
{
    static void Method(ref int i)
    {
        i = 44;
    }
    static void Main()
    {
        int val = 0;
        Method(ref val);
        // val is now 44
    }
}

传递到 ref 参数的参数必须最先初始化。这与 out 不同,out 的参数在传递之前不需要显式初始化。(请参见 out。)

尽管 refout 在运行时的处理方式不同,但它们在编译时的处理方式是相同的。因此,如果一个方法采用 ref 参数,而另一个方法采用 out 参数,则无法重载这两个方法。例如,从编译的角度来看,以下代码中的两个方法是完全相同的,因此将不会编译以下代码:

class CS0663_Example 
{
    // compiler error CS0663: "cannot define overloaded 
    // methods that differ only on ref and out"
    public void SampleMethod(ref int i) {  }
    public void SampleMethod(out int i) {  }
}

但是,如果一个方法采用 refout 参数,而另一个方法不采用这两类参数,则可以进行重载,如下所示:

class RefOutOverloadExample
{
    public void SampleMethod(int i) {  }
    public void SampleMethod(ref int i) {  }
}
备注

属性不是变量,因此不能作为 ref 参数传递。

有关传递数组的信息,请参见使用 ref 和 out 传递数组

示例

按引用传递值类型(如上所示)是有用的,但是 ref 对于传递引用类型也是很有用的。这允许被调用的方法修改该引用所引用的对象,因为引用本身是按引用来传递的。下面的示例显示出当引用类型作为 ref 参数传递时,可以更改对象本身。

class RefRefExample
{
    static void Method(ref string s)
    {
        s = "changed";
    }
    static void Main()
    {
        string str = "original";
        Method(ref str);
        // str is now "changed"
    }
}
       
out(C# 参考) 

 

out 关键字会导致参数通过引用来传递。这与 ref 关键字类似,不同之处在于 ref 要求变量必须在传递之前进行初始化。若要使用 out 参数,方法定义和调用方法都必须显式使用 out 关键字。例如:

class OutExample
{
    static void Method(out int i)
    {
        i = 44;
    }
    static void Main()
    {
        int value;
        Method(out value);
        // value is now 44
    }
}

尽管作为 out 参数传递的变量不需要在传递之前进行初始化,但需要调用方法以便在方法返回之前赋值。

refout 关键字在运行时的处理方式不同,但在编译时的处理方式相同。因此,如果一个方法采用 ref 参数,而另一个方法采用 out 参数,则无法重载这两个方法。例如,从编译的角度来看,以下代码中的两个方法是完全相同的,因此将不会编译以下代码:

class CS0663_Example 
{
    // compiler error CS0663: "cannot define overloaded 
    // methods that differ only on ref and out"
    public void SampleMethod(out int i) {  }
    public void SampleMethod(ref int i) {  }
}

但是,如果一个方法采用 refout 参数,而另一个方法不采用这两类参数,则可以进行重载,如下所示:

class RefOutOverloadExample
{
    public void SampleMethod(int i) {  }
    public void SampleMethod(out int i) {  }
}
备注

属性不是变量,因此不能作为 out 参数传递。

有关传递数组的信息,请参见使用 ref 和 out 传递数组

示例

当希望方法返回多个值时,声明 out 方法很有用。使用 out 参数的方法仍然可以将变量用作返回类型(请参见 return),但它还可以将一个或多个对象作为 out 参数返回给调用方法。此示例使用 out 在一个方法调用中返回三个变量。请注意,第三个参数所赋的值为 Null。这样便允许方法有选择地返回值。

class OutReturnExample
{
    static void Method(out int i, out string s1, out string s2)
    {
        i = 44;
        s1 = "I've been returned";
        s2 = null;
    }
    static void Main()
    {
        int value;
        string str1, str2;
        Method(out value, out str1, out str2);
        // value is now 44
        // str1 is now "I've been returned"
        // str2 is (still) null;
    }
}
 
C# 中,`ref` `out` 都是用来通过引用传递参数的关键字,它们允许方法修改调用方的变量。虽然功能相似,但在使用场景、语义安全性上有明显区别。 --- ### 一、`ref` `out` 的使用区别 #### ✅ `ref`(引用传递) - **要求**:实参在传递前必须已初始化。 - **用途**:用于希望方法能读取并修改原始变量的值。 - **语法**: ```csharp void ModifyValue(ref int value) { value = value * 2; } int x = 10; ModifyValue(ref x); // 必须先初始化 ``` #### ✅ `out`(输出参数) - **要求**:实参无需初始化,但方法内部必须在返回前赋值。 - **用途**:用于从方法中返回多个值,常用于 `TryParse` 模式。 - **语法**: ```csharp bool TryGetValue(out int result) { result = 42; // 必须在方法返回前赋值 return true; } int value; if (TryGetValue(out value)) { Console.WriteLine(value); } ``` --- ### 二、优缺点对比 | 特性 | `ref` | `out` | |------|------|------| | **是否需要初始化实参** | 是 | 否 | | **是否必须在方法内赋值** | 否(可读可写) | 是(必须赋值) | | **语义清晰度** | 表示“输入+输出” | 表示“纯输出” | | **性能** | `out` 相同(都是引用传递) | 相同 | | **适用场景** | 修改已有变量 | 返回多个值,如 `int.TryParse(out var num)` | --- ### 三、优点详解 #### ✅ `ref` 的优点: 1. **双向数据传递**:可以将值传入方法,并允许方法修改后返回。 2. **性能优化**:对于大型结构体(`struct`),避免值拷贝,提升性能(尤其是 `ref struct` 类型)。 3. **灵活性高**:适用于需要基于原值做修改的场景。 #### ❌ `ref` 的缺点: 1. **强制初始化**:调用前必须初始化变量,增加调用负担。 2. **易误用**:可能被滥用导致代码可读性下降,破坏封装。 3. **调试困难**:引用传递使变量状态变化更难追踪。 #### ✅ `out` 的优点: 1. **明确语义**:表示“此参数用于输出”,提高代码可读性。 2. **无需初始化**:调用方不需要预先赋值,简化调用。 3. **编译时保障**:C# 编译器强制要求方法必须对 `out` 参数赋值,防止未初始化错误。 #### ❌ `out` 的缺点: 1. **只能用于输出**:不能在方法内读取其原始值(否则编译报错)。 2. **语法略显冗余**:C# 7.0 之前不支持 `out var`,声明变量较繁琐。 3. **现代替代方案出现**:元组(`ValueTuple`)等提供了更优雅的多返回值方式。 --- ### 四、性能说明 - `ref` `out` 在底层都传递的是地址(引用),不会复制值。 - 对于值类型(尤其是大 `struct`),使用 `ref/out` 可显著减少内存拷贝。 - 对于引用类型(class),传递的是引用的副本,`ref/out` 用于修改引用本身(即指向另一个对象)。 ```csharp // 示例:通过 ref 改变引用本身 void ReplaceObject(ref MyClass obj) { obj = new MyClass { Name = "New" }; } MyClass myObj = new MyClass { Name = "Old" }; ReplaceObject(ref myObj); // myObj 现在指向新对象 ``` --- ### 五、最佳实践建议 1. **优先使用 `out` 实现 `TryXXX` 模式**(如 `int.TryParse`)。 2. **大型结构体传参考虑 `ref` 提升性能**(配合 `readonly ref` 更安全)。 3. **避免过度使用 `ref/out`**,优先考虑返回对象或元组。 4. **C# 7+ 推荐使用元组返回多值**: ```csharp (bool success, int value) TryParse(string input) { if (int.TryParse(input, out int v)) return (true, v); return (false, 0); } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值