C#中ref、out、in关键字的用法是什么?
在C#中,大多数方法可以有零个或多个参数,这些参数定义必须提供给方法的数据。任何调用该方法的代码都必须将数据(称为参数)传递给该方法。一个方法将它的输入声明为参数,它们是通过以参数的形式调用代码来提供的。
例如,考虑以下方法和后续方法调用。
static void Greet(string greeting){ Console.WriteLine(greeting); } ... Greet("Hello");
在上面的例子中,greeting是Greet()方法的参数,“Hello”是传递给方法的参数。
当您调用一个方法并传递参数时,它们是按值传递的,这意味着在传递给该方法时会创建该值的副本。对方法内部参数所做的任何更改都不会反映在原始变量上。
using System; int number = 8; Increase(number); Console.WriteLine(number); //打印8 static void Increase(int num){ num = num + 1; Console.WriteLine(num); //打印9 }
当您传递引用类型变量时,例如对象,C#仍然按值复制引用,因为变量保存的是引用,而不是实际对象。因此,即使传递了引用变量的副本,它们也指向内存中的同一个对象。因此,方法内部的变量对对象所做的任何更改对方法外部的变量都是可见的。
using System; var john = new User{ Name = "John", Salary = 50000 }; Promote(john); Console.WriteLine(john.Salary); //打印60000 static void Promote(User u){ u.Salary += 10000; Console.WriteLine(u.Salary); //打印60000 }
但是,如果您在方法内部更改变量的值本身,则该更改不会反映在方法外部,因为仅更改了副本,而不是实际对象。例如,我们可以在方法内部将参数赋值为null,而实际变量将保持不变。
using System; var john = new User{ Name = "John", Salary = 50000 }; Promote(john); Console.WriteLine(john.Salary); //打印50000 static void Promote(User u){ u = null; }
C#允许使用三种不同的修饰符关键字,您可以使用它们来控制方法的参数。
ref修饰符
C#允许您使用ref修饰符通过引用传递参数。传递的变量是属于引用类型还是值类型并不重要。参数和自变量都将引用相同的内存位置。
在下面的示例中,将参数u设置为null也会使john变量为null,从而导致null引用异常。
例子
using System; var john = new User{ Name = "John", Salary = 50000 }; Promote(ref john); Console.WriteLine(john.Salary); //抛出System.NullReferenceException static void Promote(ref User u){ u = null; }
使用ref修饰符传递的参数必须在传递之前进行初始化。
输出修饰符
它类似于ref修饰符,除了
参数在进入函数之前不必初始化
参数在从函数中出来之前必须被初始化(分配给)。
在下面的示例中,Hire函数初始化一个通过out修饰符传递给它的新用户对象。请注意,变量是在调用Hire方法时动态声明的变量john。
using System; Hire(out User john); Console.WriteLine(john.Salary); //打印50000 static void Hire(out User u){ u = new User{ Name = "John", Salary = 50000 }; }
与ref修饰符一样,由out修饰符标记的变量也是通过引用传递的。
通常使用out变量从函数中获取多个返回值,如下所示-
using System; var john = new User{ Name = "John", Salary = 50000 }; bool shouldPromote = Raise(john.Salary, out double hike); Console.WriteLine(shouldPromote); //真的 Console.WriteLine($"Hike Amount = {hike}"); //打印5000 static bool Raise(int salary, out double hike){ hike = salary * 0.1; return hike < 7000; }
in修饰符
它类似于ref和out参数,不同之处在于接受in参数值的方法不能修改该参数。如果尝试,C#编译器会生成编译时错误。
的在参数保存来自复制大值类型的存储器的参数变量,同时防止物体意外修改的编译器。这使得它们在将大值类型传递给方法时非常有用。
var point = new Coord(); Verify(point); static void Verify(in Coord c){ //错误:无法分配给变量“inCoord”,因为它是只读变量 c = new Coord(); } struct Coord{ public int X; public int Y; }
示例
using System; class Program{ static void Main(){ int number = 8; Increase(number); Console.WriteLine(number); //打印8 var john = new User { Name = "John", Salary = 50000 }; Promote(john); Console.WriteLine(john.Salary); //打印60000 Leave(john); Console.WriteLine(john.Salary); //打印60000 LeaveByRef(ref john); Console.WriteLine(john?.Salary); //什么都不打印 User dave; Hire(out dave); Console.WriteLine(dave.Salary); //打印50000 } static void Increase(int num){ num = num + 1; Console.WriteLine(num); //打印9 } static void Promote(User u){ u.Salary+= 10000; Console.WriteLine(u.Salary); //打印60000 } static void Leave(User u){ u = null; } static void LeaveByRef(ref User u){ u = null; } static void Hire(out User u){ u = new User{ Name = "John", Salary = 50000 }; } } class User{ public string Name { get; set; } public int Salary { get; set; } }输出结果
9 8 60000 60000 60000 50000