C#中关于引用参数ref关键字的使用和本质论

标签: c#

本篇博客主要介绍的是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());
        }
    }
}

运行结果:
运行结果

如有错误,请帮忙指正,谢谢!

版权声明:本文为a_asdfgh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a_asdfgh/article/details/78407451

智能推荐

关于C中const关键字的使用

Const 关键字 例:const int a = 5 等同于 int const a = 5; 其中a值最终未改变,改变的是指针p所指向a的值,但从道义上来讲,这样写是不可取的;如果变量a被const所修饰,那么定义指向a的指针也应当用const所修饰。 如果这样写程序就会报错...

【C#本质论 六】类-从设计的角度去认知

从这一章开始,基本就脱离了结构化编程的思维,所以我的博客排版方式也会摒弃之前的节选方式,而是深入的去理解和描述内容,而这本书给我带来的惊喜就是:能够从设计的角度去帮助我理解为何类要如此设计,其设计缘由是什么,我觉得这一点远比单纯的告诉你怎么用更重要!感觉这一章的干货特别多,可以让我的面向对象思维上一个小小的台阶,这里将自己的学习笔记以及自己的理解整理分享出来,希望对大家都有所帮助,还有一点要说明下...

【C#本质论 八】类-从设计的角度去认知(多态)

其实称本篇为多态还是有些牵强,因为在类的继承中也是存在多态的,例如我们的重写机制,但可以设想这样一个场景:飞行这个动作,鸟可以飞行,飞机可以飞行,而飞机其实和鸟没有父子关系的,他们共同拥有的是行为:飞行。所以本篇博客着重介绍这一点:如何通过接口来处理行为一致(横向关系)而非一脉相承(纵向关系)的关系。本篇的结构如下: 接口定义 为什么要有接口?在介绍了类和抽象类(我的感觉就是抽象类属于类和接口的中...

C#中参数为引用类型加ref和不加的区别

首先说说两种数据类型:值类型和引用类型的区别 值类型 包含int、double、char、bool、struct、还有枚举enum, 声明一个值类型的变量会在栈上分块空间用来存储变量的值,如int a= 1,a的值直接存储在栈上 引用类型 包含类、string、object,Interface,引用类型的对象或实例存储在堆上,而栈上存储的是指向这个堆的地址如ClassA s= new ClassA...

关于GC,this的本质和static关键字所需要注意的点

gc相关的算法 1.引用计数算法:在堆中每个对象都有一个引用计数,被引用一次,计数加1,被引用变量值变为null,则计数减1,直到计数为0,则表示变成无用对象,优点是算法简单,缺点是“循环引用的无用对象”无法识别 s1和s2互相引用,导致引用计数永远不为0 ,但是实际已无用,却没有办法识别。 2.引用可达法(根搜索算法) 程序把所有的引用关系看作一张图,从一个节点GC RO...

猜你喜欢

ref和out关键字的用法和区别

前言 ref和out关键字对于初学者来说,即用的少又难理解。看过多篇网上的ref和out博客后决定自己整理一份ref 和 out 关键字的用法和区别 ref 和 out 的用法 ref 和 out 最常见的用法:当需要一个方法返回多个参数的时使用ref和out关键字。 例如:我们需要通过一个方法计算int型a、b两个值 我们先看看ref的示例: 输出结果为a=50,b=150, 这样我们就能简单的...

冒泡排序,及改进方式,性能优化400%>>>附图解加源码

首先源码附上,源码中带有注释,看不懂没关系,源码后面附带图解,最后附上代码效率提升图 源码如下: 方案一:其实实现很简单,两层循环,每次内层迭代出最大的一个值,将其放入数组最后一个位置,外层循环的末端便往前移一位。其原理如下图 方案一代码块: 方案二:优化改进 仔细观察上面的图,我们不难发现当迭代到图下这样的情况时,其实已经全排序好了,但是我们还是需要对它进行1,2,3,4,5,6的迭代,这些情况...

css3常用选择器

先介绍一下基本选择器,如下几种 *:通配符。选择页面的所有元素 E:元素选择器。对页面的一些元素进行选择,如p,div,li等 .class:类选择器 #id:id选择器。一个Id在一个页面中只能选择,这是和类选择器的区别 E F:后代选择器。如ul li。选中ul下的li E>F:子元素选择器。如下,可以用 h1 > strong {color:red;}使h1的子元素strong变...

python is not set from command line or npm configuration

问题 猜测原因  python版本冲突 解决方案  去官网找到最新版本python 点击下载,注意记住你下载的目录,下载好以后,打开这个目录下的文件夹,Python310, 整个文件夹复制一下 在项目里运行npm config list --json 找到python的路径   我的是 C:\\Python27\\python.exe 说明项目里用的还是py...

Java8 Stream流操作

前言 我们常常需要将一个容器转化成另一个容器,或是对这个容器中的数据进行批量处理,这时使用Stream流可以大大减少我们的工作量。 1 Stream概述 Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。 那么什么是Stream? Stream将要处理的元素集合看作一种流,在流的过程中,借助St...