当前位置: 首页 > news >正文

C# - 值类型、引用类型走出误区,容易错误的说法

C# - 值类型、引用类型&走出误区,容易错误的说法

 

1. 值类型与引用类型小总结

1)对于引用类型的表达式(如一个变量),它的值是一个引用,而非对象。

2)引用就像URL,是允许你访问真实信息的一小片数据。

3)对于值类型的表达式,它的值是实际的数据。

4)有时,值类型比引用类型更有效,有时恰好相反。

5)引用类型的对象总是在堆上,值类型的值既可能在栈上,也可能在堆上,具体取决于上下文。

6)引用类型作为方法参数使用时,参数默认是以‘值传递’方式来传递的,但值本身是一个引用。

7)值类型的值会在需要引用类型的行为时装箱;拆箱则是相反的过程。

 

2. 误区一 结构是轻量级的类

这个误解存在着多种形式。有人认为值类型不能或不应有方法或有其他意义的行为:它们应作为简单的数据转移类型来使用,只应该有public字段或简单的属性。对于这种说法,一个非常典型的反例就是Datetime类型。DateTime作为值类型来提供是很有道理的,因为它非常适合作为和数字或字符相似的一个基本单位来使用。另外,它也理应被赋予对它的值执行计算的能力。换个角度来看这个问题,是数据转移类型一般都是引用类型。总之,具体应该如何决定,应取决于需要的是值类型的语义,还是引用类型的语义,而不是取决于这个简单类型与否。

还有一些人认为值类型之所以显得比引用类型‘轻’,是因为性能,事实是在某些情况下,值类型很能‘干’:它们不需要垃圾回收,(除非被装箱)不会因类型标识而产生开销,也不需要解引用。但在其他方面,引用类型显得更‘能干’:在传递参数、赋值、将值返回和执行类似的操作时,只需复制4或8字节(需要看运行的是32位或是64位CLR),而不是复制全部数据。假定ArrayList是一个所谓‘纯的’值类型,那么将一个ArrayList表达式传给一个方法时,就得复制它的所有数据!几乎在所有情况下,性能问题都不是根据这种判断来决定的。瓶颈从来都不是想当然的,在你根据性能进行设计之前,需要衡量不同的选择。

值得注意的是,将这两者相结合也不能解决问题:类型(不管是类还是结构)拥有多少方法并不重要,每个实例所占用的内存不会受到影响。(代码本身会消耗内存,但这只会发生一次,而不是每个实例都发生)

 

3. 误区二 引用类型保存在堆上,值类型保存在栈上

这个误区主要应归咎于转述这句话的人根本没有动脑筋。这一部分是正确的,引用类型的实例总是在堆上创建的。但第二部分就有问题了。

前面讲述过,变量的值是在它声明的位置存储的。所以,假定一个类中又一个int类型的实例变量,那么在这个类中的任何对象中,该变量的值总是和对象中的其他数据在一次,也就是在堆上。只有局部变量(方法内部声明的变量)和方法参数在栈上。对于C#2或以上版本,很多局部变量并不完全存放在栈中。

 

4. 误区三 对象在C#中默认是通过引用传递的

这或许是传播得最广的一个误区了。统一说这句话的人一般知道C#实际的行为是什么,但不知道‘引用传递pass by reference’的真正意思是什么。可惜,那些真正知道引用传递是什么意思的人,在听到这句话时会被完全搞糊涂。

‘引用传递’的正式定义相当复杂,要涉及坐值(1-values)和类似的计算机科学术语。但最重要的一点是,假如以引用传递的方式来传送一个变量,那么调用的方法可以通过更改其参数值,来改变调用者的变量值。现在请记住,引用类型变量的值是引用,而不是对象本身。不需要按引用来传递参数本身,就可以更改该参数的引用的那个对象的内容。例如,下面的方法更改了相关对象StringBuilder的内容,但调用者的表达式引用的仍然是之前的那个对象:

        public void AppendHello(StringBuilder builder)
        {
            builder.Append("Hello");
        }

调用这个方法时,参数值(对StringBuilder的一个引用)是以值传递pass by value 的方式传递的。如果想在方法内部更改builder变量的值,如执行builder=null 语句,调用者看不见这个改变,刚好跟错误认识相反。

有趣的是,这种错误说法中,不仅引用传递的说法有误,而且 对象传递的说法也存在问题。无论引用传递还是值传递,永远不会传递对象本身。涉及一个引用类型时,要么以引用传递的方式传递变量,要么以传值的方式传递参数值(引用)。最起码,这回答了‘当null作为一个传值参数的值来使用时会发生什么’的问题。假如传递的是对象,这是就会出问题,因为没有一个对象可供传递!相反,null引用会采用和其他引用一样的值传递方式传递。

 

可以关注本人的公众号,多年经验的原创文章共享给大家。

posted on 2017-01-06 16:45 alun-chen 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/alunchen/p/6256903.html

相关文章:

  • 常用网址
  • python 04
  • Python程序-离散和线性图形
  • MongoDB安全事件的一些思考
  • java之多线程的理解
  • json常用方法介绍
  • NSOperation 开发
  • 批量更新MongoDB的列。
  • SOA是什么
  • Apache HttpCore (理解IO基础)
  • 启动eclipse时出现“Failed to load the JNI shared library jvm.dll”错误及解决
  • 软件项目技术点(3)——多画布职责分离
  • 浅尝springboot中的Actuator包(一)
  • 【使用教程】论Windows下必备的抓包工具Fiddler2如何安装证书(查看Https)
  • RPC学习
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 【笔记】你不知道的JS读书笔记——Promise
  • canvas 五子棋游戏
  • golang中接口赋值与方法集
  • HTTP那些事
  • java中的hashCode
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • php中curl和soap方式请求服务超时问题
  • Spring-boot 启动时碰到的错误
  • 初识 webpack
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 聚簇索引和非聚簇索引
  • 聚类分析——Kmeans
  • 开源SQL-on-Hadoop系统一览
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 深度学习在携程攻略社区的应用
  • 收藏好这篇,别再只说“数据劫持”了
  • 数据结构java版之冒泡排序及优化
  • 为什么要用IPython/Jupyter?
  • 物联网链路协议
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • #QT(智能家居界面-界面切换)
  • (06)Hive——正则表达式
  • (11)MATLAB PCA+SVM 人脸识别
  • (4.10~4.16)
  • (70min)字节暑假实习二面(已挂)
  • (pytorch进阶之路)扩散概率模型
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (附源码)ssm高校实验室 毕业设计 800008
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • .Net Remoting常用部署结构
  • .Net Web窗口页属性
  • @Autowired注解的实现原理
  • @RequestMapping处理请求异常
  • @selector(..)警告提示
  • [ Linux ] Linux信号概述 信号的产生
  • [].slice.call()将类数组转化为真正的数组
  • [2013][note]通过石墨烯调谐用于开关、传感的动态可重构Fano超——
  • [20171101]rman to destination.txt
  • [acm算法学习] 后缀数组SA