本篇博客主要介绍的是ref关键字的使用和本质:
- 1.ref关键字的使用
- 2.ref关键字的本质
1.ref的使用
-ref的作用主要是让值类型能够像引用类型一样在方法中进行了值的改变,在方法结束之后,能够将值保存下来。看代码:
(1)没有使用ref关键字:
//两个值类型的变量
int x = 1;
int y = 2;
//调用ChangeValue(int a,int b)方法,将x,y传入进行
Console.WriteLine("原来的值:x ={0},y = {1} ",x,y);//原来的值:x =1,y = 2
Console.WriteLine("============================")
ChangeValue(x,y);
Console.WriteLine("改变后的值:x ={0},y = {1} ",x,y);//改变后的值:x =1,y = 2
Console.WriteLine("============================")
//一个将传入的两个参数进行相互交换的方法
public void ChangeValue(int a,int b){
Console.WriteLine("原来的值:a ={0},b = {1} ",a,b)//原来的值:a =1,b=2
Console.WriteLine("============================")
int temp = a;
a = b;
b = a;
Console.WriteLine("改变后的值:a ={0},b = {1} ",a,b)//改变后的值:x=2,y=1 没有改变
Console.WriteLine("============================")
}
(2)使用ref关键字:
//两个值类型的变量
int x = 1;
int y = 2;
//调用ChangeValue(int a,int b)方法,将x,y传入进行
Console.WriteLine("原来的值:x ={0},y = {1} ",x,y);//原来的值:x =1,y = 2
Console.WriteLine("============================")
ChangeValue(ref x,ref y);
Console.WriteLine("改变后的值:x ={0},y = {1} ",x,y);//改变后的值:x =2,y = 1 改变了
Console.WriteLine("============================")
//一个将传入的两个参数进行相互交换的方法
public void ChangeValue(ref int a,ref int b){
Console.WriteLine("原来的值:a ={0},b = {1} ",a,b)//原来的值:a =1,b=2
Console.WriteLine("============================")
int temp = a;
a = b;
b = a;
Console.WriteLine("改变后的值:a ={0},b = {1} ",a,b)//改变后的值:x=2,y=1
Console.WriteLine("============================")
}
通过上面的两个例子可以看出ref的基本用处。
2.ref的本质
通过上面的例子,我们看到用了ref之后,值类型的x,y也可以像引用类型一样吗,保存住在方法中的修改。纠其原因:
(1)值类型是存储在栈中,栈的保存的就是当前的值;而引用类型了是保存在堆里面的,栈里面保存的是其在堆里面的地址。
(2)不加ref的参数,在内存中表现为:为形参开辟一个栈的内存空间,然后将实参的值存储进去。这样对于值类型而言:就是原来值的一个副本,形参与实参之间也就没有什么直接的联系了。但是对于引用类型而言:同样是将实参的值存储到形参的空间中,但是由于实参存储的一个对象在堆里面的地址,所以这样形参和实参指向的都是一个存储在堆里面的对象。
(3)加了ref之后:系统不会再为形参开辟空间,而是将形参指示为实参的一个别名,这样不管值类型还是引用类型,形参只是实参的另一个名字而已,指向的是同一个栈中的空间。这样在方法中对形参的修改也就是外面的实参的修改了。
扩展:ref对引用类型的影响
见代码:
namespace _01BeginOne
{
class People
{
public int Age { set; get; }
public string Name { set; get; }
public People(int age,string name)
{
this.Age = age;
this.Name = name;
}
public override string ToString() => "姓名是:" + this.Name + " 年龄是:" + this.Age;
}
class Program
{
static void Main(string[] args)
{
//1.引用类型作为值参数进行传递:在方法里面将参数改变,不会影响到外部的原对象。
//说明在值参数进行传递的时候,实参和形参都是有各自的内存空间的
#region 引用类型作为值参数进行传递
People xiaoming = new People(21, "小明");
Console.WriteLine("未调用方法之前:"+ xiaoming.ToString());
ChangeAge(xiaoming);
Console.WriteLine("调用方法之后:" + xiaoming.ToString());
#endregion
Console.WriteLine("==================");
//2.引用类型作为引用参数进行传递,在方法里面将传进去的参数进行重新初始化,结果会影响到最初外部的原对象。
//说明在引用参数进行传递的时候,形参其实没有在栈中开辟空间,只是实参的一个别名。
#region 引用类型作为引用参数进行传递:ref
People lanyangyang = new People(21, "懒洋洋");
Console.WriteLine("未调用方法之前:" + lanyangyang.ToString());
ChangeAge(ref lanyangyang);
Console.WriteLine("调用方法之后:" + lanyangyang.ToString());
#endregion
Console.ReadKey();
}
static void ChangeAge(People people)
{
people.ToString();
Console.WriteLine("======改变people引用的对象=======");
People newPeople = new People(20, "喜洋洋");
people = newPeople;//将新对象赋值给参数对象
Console.WriteLine(people.ToString());
}
static void ChangeAge( ref People people)
{
people.ToString();
Console.WriteLine("======改变people引用的对象=======");
People newPeople = new People(20, "灰太狼");
people = newPeople;//将新对象赋值给参数对象
Console.WriteLine(newPeople.ToString());
}
}
}
运行结果: