引用类型就理应这样吗?——浅析“引用”

2023-12-04 02:10
文章标签 类型 引用 浅析 理应

本文主要是介绍引用类型就理应这样吗?——浅析“引用”,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C#中引用类型继承自System.Object.引用类型的对象的值存放在托管堆上,在栈中只存放对它的索引(地址)。因为这个原因,引用类型对象之间的赋值操作默认情况下都是浅拷贝,只拷贝栈中的一份索引,它们指向一块相同的托管堆内存。如Person p=new Person();Person p1=p;第二个赋值语句的意思就是让p1指向p所指向的对象,即二者共同指向一个Pernson的实例new Person()。可是这句话说起来简单,但是应该不是那么好理解的,至少对我来说很长一段时间是这样。下边谈谈本人的拙见,学的东西有限,欢迎指正。

我们可以问自己几个问题:

1p1p既然指向同一个对象,那么他们本身也就是同一个对象?

2、既然他们指向同一个对象,那么对p指向的对象做了修改也会导致p1指向的对象改变?

3、如果问题二答案是肯定的,那么也就是说操作p对象也会同样操作p1对象?

4、如果问题三的答案是肯定的,那么对引用类型对象的任意操作就会同时更改另一个和它具有相同类型并且指向同一个对象的对象的值?也就是说ref关键字对于引用类型的参数传递没有意义?(因为按照这种逻辑引用类型的变量在方法中的操作同样会反应到方法外边来,无需使用ref关键字也能达到这样的效果)带着这几个问题,浅析下c#中引用类型的“引用”。

         先贴段代码吧。

    class Person

    {

        publicPerson(int age, stringname) { this.age = age; this.name = name; }

        public int age;

        public string name;

        public void DoSomthing() { Console.WriteLine("我的name是{0},age是{1}", name, age); }

}

 

    class Program

    {

        static void Main(string[]args)

        {

            Personp = new Person(100,"张三");

            TestRef(p);

            p.DoSomthing();

        }

 

        static void TestRef(Personp)

        {

            p.name = "李四";

        }

}

运行结果:                             

                        图一

1、保持其它部分不变,将方法TestRef修改为以下代码

        static void TestRef(Personp)

        {

            p = newPerson(99,"李四");

        }

 

运行结果: 

图二

2、保持其它部分不变,将方法TestRef修改为以下代码

        static void TestRef(ref Person p)

        {

            p = newPerson(99,"李四");

        }

 

运行结果: 

                            图三

 

现在解释上边代码的运行结果并回答开头的四个问题。

三段代码中p都是引用类型Person的对象,调用方法TestRef的本质是将Main方法中实参p的值赋值给TestRef方法的形参p,这种赋值在前边已说过,只是浅层拷贝,两个p都指向同一个对象,所以在方法中对p指向的对象进行操作 p.name= "李四" 将同样反应到方法外边来。但即使是这样,两个p并不是同一个对对象,也就是说Person.ReferenceEquals对于这两个对象来说是false。说的直白点,形参p在栈中地址和实参p在栈中的地址并不相同。一个很简单的例子,Personp=new Person();Person p1=p;p=null;在进行了这段操作之后难道p1也为null?答案显然是否定的。

这样就解释了图一和图二的运行结果以及问题一的答案。

至于图三,因为加了ref关键字,所以在实参和形参传值时的并不是做简单的拷贝,而是让形参直接和实参共用同一段堆内存,也就是说他们现在是同一个对象,这点就像是c语言中指针存放的地址和它指向的变量的地址的关系一样(注意:并不是指针本身的地址而是它存放的地址也就是他的值)。所以形参和实参完全是等价的。这就是ref关键字的价值所在。

这样也回答了问题四。

关于问题二,答案时肯定的,因为图一中修改了形参p所指向的对象的值,也同时修改了实参p所指向对象的值。但是之中修改也是有限制的。假如当修改p1所指对象的值时p已经指向了一个新的对象,那么这个修改就不会在p中反应出来,所以如果要想将引用类型对象的值的修改反应到方法体外边来而又不想用ref关键字,那么请勿在方法体内改变形参的指向,诸如 p=new Personp=null之类的操作都会改变p的指向。这样的操作对方发体外的实参是无效的,因为他们并不是同一个对象,他们仅仅是指向同一个对象而已。

关于问题三,其实在上边已作了回答。答案当然是否定的,它们又不是同一个对象,操作对象p和对象p1毛线关系都没有。只是说当pp1指向同一个对象时,对p的操作有可能是对p所指向对象的操作(如p.name=”李四,但也有可能不是,如p=new Person(),那么也当然会操作到p1所指向的对象,这样就会反应到p对象上来,仅此而已。

补充一点,对引用类型对象的赋值操作 如:Personp=new person(),实际的做的事情大概是在托管堆上创建Person 类的对象new Person()并返回该对象的指针,然后在栈上创建Person类型的对象的引用,并使该引用的值为刚刚返回的指针值。

综上所述,我觉得说xx对象所指向的对象,追根朔源指的应该就是类似于new Person()的这个对象,其它对象可能会指向xx这个对象所指向的对象,即new Person。要搞清楚引用类型的各种细节,起码得搞清哪个对象是哪个。

转载于:https://www.cnblogs.com/eval_anrong/p/3527691.html

这篇关于引用类型就理应这样吗?——浅析“引用”的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/451717

相关文章

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

浅析如何保证MySQL与Redis数据一致性

《浅析如何保证MySQL与Redis数据一致性》在互联网应用中,MySQL作为持久化存储引擎,Redis作为高性能缓存层,两者的组合能有效提升系统性能,下面我们来看看如何保证两者的数据一致性吧... 目录一、数据不一致性的根源1.1 典型不一致场景1.2 关键矛盾点二、一致性保障策略2.1 基础策略:更新数

浅析Java如何保护敏感数据

《浅析Java如何保护敏感数据》在当今数字化时代,数据安全成为了软件开发中至关重要的课题,本文将深入探讨Java安全领域,聚焦于敏感数据保护的策略与实践,感兴趣的小伙伴可以了解下... 目录一、Java 安全的重要性二、敏感数据加密技术(一)对称加密(二)非对称加密三、敏感数据的访问控制(一)基于角色的访问

Java资源管理和引用体系的使用详解

《Java资源管理和引用体系的使用详解》:本文主要介绍Java资源管理和引用体系的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Java的引用体系1、强引用 (Strong Reference)2、软引用 (Soft Reference)3、弱引用 (W

浅析如何使用xstream实现javaBean与xml互转

《浅析如何使用xstream实现javaBean与xml互转》XStream是一个用于将Java对象与XML之间进行转换的库,它非常简单易用,下面将详细介绍如何使用XStream实现JavaBean与... 目录1. 引入依赖2. 定义 JavaBean3. JavaBean 转 XML4. XML 转 J

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

浅析Java中如何优雅地处理null值

《浅析Java中如何优雅地处理null值》这篇文章主要为大家详细介绍了如何结合Lambda表达式和Optional,让Java更优雅地处理null值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录场景 1:不为 null 则执行场景 2:不为 null 则返回,为 null 则返回特定值或抛出异常场景

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

Pydantic中Optional 和Union类型的使用

《Pydantic中Optional和Union类型的使用》本文主要介绍了Pydantic中Optional和Union类型的使用,这两者在处理可选字段和多类型字段时尤为重要,文中通过示例代码介绍的... 目录简介Optional 类型Union 类型Optional 和 Union 的组合总结简介Pyd

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数