关键字ref、out、in的区别

本文详细解释了C#中的ref、out和in三种参数传递方式的区别,包括引用传递、值传递的特点,以及ref和out在方法中的行为差异。

在C#中,ref、out和in是三种不同的参数传递方式,并且都是引用传递

引用传递:表示传递的是对象的引用,而不是对象本身。

ref

1)参数在传入方法前必须初始化

2)方法内可以读取和修改参数值,修改会影响原始变量

public void Main()
            {
                int number = 10;
                Debug.Log($"初始值: {number}");

                ModifyNumber(number);
                Debug.Log($"调用Common ModifyNumber后: {number}");//10

                ModifyNumber(ref number);
                Debug.Log($"调用ref ModifyNumber后: {number}");//20

                ModifyNumber(number);
                Debug.Log($"调用Common ModifyNumber后: {number}");//20
            }

            void ModifyNumber(ref int value)
            {
                Debug.Log($"调用ref ModifyNumber前: {value}");//10
                value *= 2;
            }

            void ModifyNumber(int value)
            {
                Debug.Log($"调用Common ModifyNumber前: {value}");//10、20
                value *= 3;
            }

out

1)参数在传入方法前不需要初始化

2)方法内必须为参数赋值

3)常用于从方法返回多个值

// 需求:我们想从一个方法同时获取学生的姓名和年龄  
        public class Example_out  
        {  
            public void Main()  
            {  
                // ✓ 使用 out 可以同时获取多个值  
                GetStudentInfo(1001, out string name, out int age);  
                Debug.Log($"学生姓名:{name},年龄:{age}");  

                // ❌ 如果不用 out,我们就需要多次调用方法  
                string name2 = GetStudentName(1001);  
                int age2 = GetStudentAge(1001);  
            }  

            // 使用 out 一次性返回多个值  
            void GetStudentInfo(int id, out string name, out int age)  
            {  
                // 模拟从数据库获取数据  
                name = "张三";  // out 参数必须在方法内赋值  
                age = 18;      // out 参数必须在方法内赋值  
            }  

            // 不使用 out 就需要写多个方法  
            string GetStudentName(int id) { return "张三"; }  
            int GetStudentAge(int id) { return 18; }  
        }

out与ref的对比

//out&ref对比
        public void Main()  
        {  
            // 1. ref 必须先初始化变量  
            int x = 0;            // ref 需要初始化  
            MethodWithRef(ref x); // 必须用已初始化的变量  

            // 2. out 不需要初始化  
            MethodWithOut(out int y); // 直接声明变量  
            // 或者  
            int z;  
            MethodWithOut(out z);     // 使用未初始化的变量也可以  
        }  

        void MethodWithRef(ref int number)  
        {  
            // ref 参数可以不赋值  
            if (number > 0)  
            {  
                number = 100;  
            }  
        }  

        void MethodWithOut(out int number)  
        {  
            // out 参数必须赋值  
            number = 100; // 如果不赋值会编译错误  
        }

多个返回值Tuple与out的对比 

C#也可以使用元组来处理多个返回值。

学习out的时候,忽然想到了,就顺手记一下~ 

//处理多个返回值的2种方案
        public class Example  
        {  
            public void Main()  
            {  
                // 1. 使用 out 参数  
                GetPersonInfoWithOut("John", out string title, out int age);  
                Debug.Log($"Out方式: {title}, {age}");  

                // 2. 使用元组  
                var (tupleTitle, tupleAge) = GetPersonInfoWithTuple("John");  
                Debug.Log($"元组方式: {tupleTitle}, {tupleAge}");  
        
                // 2.1 也可以直接使用元组  
                var person = GetPersonInfoWithTuple("John");  
                Debug.Log($"元组方式: {person.title}, {person.age}");  
            }  

            // 使用 out 参数的方法  
            void GetPersonInfoWithOut(string name, out string title, out int age)  
            {  
                title = "Mr.";  
                age = 30;  
            }  

            // 使用元组的方法  
            (string title, int age) GetPersonInfoWithTuple(string name)  
            {  
                return ("Mr.", 30);  
            }  
        }

看起来很混乱的一篇关于ref、out、tuple的代码 


in

C#7.2引入的新特性

1)用于传递只读引用

2)参数在方法内不能被修改

3)主要用于提高性能,特别是处理大型结构体

//只读、可传递给其他in参数
public void ProcessData(in Point point)  
{  
    // ❌ 不能直接修改参数  
    // point.X = 100; // 编译错误  

    // ✓ 可以读取参数  
    Console.WriteLine($"X: {point.X}, Y: {point.Y}");  

    // ❌ 不能传递给非 in 参数  
    // ModifyPoint(point); // 编译错误  

    // ✓ 可以传递给其他 in 参数  
    PrintPoint(in point);  
}  
//使用场景
// ✓ 适合使用 in 的场景:大型结构体  
public struct BigStruct  
{  
    public double Field1;  
    public double Field2;  
    // ... 更多字段  
}  

public double ProcessBigStruct(in BigStruct data)  
{  
    return data.Field1 + data.Field2;  
}  

// ❌ 不适合使用 in 的场景:小型结构体或引用类型  
public void ProcessSmallStruct(in int number) { } // 不必要  
public void ProcessString(in string text) { }     // 不必要  

 对于“传递”的深入理解

“ ref、out、in这些关键字确实都是按引用传递,但它们传递的是变量的内存地址引用,而不是创建新的变量。

这句话中“传递的是变量的内存地址引用”怎么理解呢?

值传递:

void Method1(int x) {  
    x = 20;  // 修改的是副本  
}  

int a = 10;  
Method1(a);  // a 还是 10  

内存示意:

栈内存:  
a (地址0x1000) → 值: 10  
方法内的 x (地址0x2000) → 值: 20  // 复制了一份,在新地址  

引用传递:

void Method2(ref int x) {  
    x = 20;  // 修改原始变量  
}  

int a = 10;  
Method2(ref a);  // a 变成 20  

 内存示意:

栈内存:  
a (地址0x1000) → 值: 10 → 20  
方法内的 x → 指向地址0x1000  // x 获得了 a 的地址,而不是值的副本  

对引用类型的引用传递:

class MyClass {  
    public int Value;  
}  

void Method3(ref MyClass obj) {  
    obj = new MyClass();  // 改变引用本身  
    obj.Value = 20;       // 修改新对象的值  
}  

MyClass mc = new MyClass();  
mc.Value = 10;  
Method3(ref mc);  // mc 指向新对象  

 内存示意:

栈内存:  
mc (地址0x1000) → 指向堆地址0xA000  
方法内的 obj → 指向地址0x1000  // obj 获得了 mc 的地址  

堆内存:  
0xA000 → { Value: 10 }  // 原始对象  
0xB000 → { Value: 20 }  // 新对象  

对比不使用 ref 的引用类型传递:

void Method4(MyClass obj) {  
    obj = new MyClass();  // 只改变局部引用  
    obj.Value = 20;       // 修改新对象的值  
}  

MyClass mc = new MyClass();  
mc.Value = 10;  
Method4(mc);  // mc.Value 还是 10  

 内存示意:

栈内存:  
mc (地址0x1000) → 指向堆地址0xA000  
方法内的 obj (地址0x2000) → 最初指向0xA000,后指向0xB000  // 复制了引用  

堆内存:  
0xA000 → { Value: 10 }  // 原始对象  
0xB000 → { Value: 20 }  // 新对象(方法结束后被垃圾回收)  


总结 

初始化要求

1)ref:必须初始化

2)out:不需要初始化

3)in:必须初始化

方法内部行为

1)ref:可读可写

2)out:必须写(赋值)

3)in:只读

使用场景

1)ref:需要在方法内修改参数值时

2)out:需要返回多个值时

3)in:传递大型结构体提升性能时

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值