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

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 Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G

Oracle type (自定义类型的使用)

oracle - type   type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要特殊的格式, 如将name定义为(firstname,lastname)的形式,我们想把这个作为一个表的一列看待,这时候就要我们自己定义一个数据类型 格式 :create or repla

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u

按揭贷款类型

按揭贷款可以根据不同的分类标准分为多种类型。以下是按揭贷款的一些常见分类: 按贷款利率分类: 固定利率按揭(Fixed Rate Mortgage, FRM):在整个贷款期间,利率保持不变,这意味着每月还款额也是固定的。浮动利率按揭(Adjustable Rate Mortgage, ARM):贷款利率随市场利率的变化而调整,通常有一个基准利率加上一定的浮动点数。 按还款方式分类: 等额本息(