NET基础(4):引用类型和值类型
CLR支持两种类型:引用类型和值类型。虽然FCL的大多数类型都是引用类型,但程序员用的最多的还是引用类型,引用类型总是从托管堆分配,c#的new操作符返回对象内存地址-即指向对象数据的内存地址。使用引用类型必须注意性能问题。首先要认清楚以下4个方面:
1、内存必须从托管堆分配。
2、堆上分配的每个对象都有一些额外的成员,这些成员必须初始化。
3、对象中的其它字节(为字段而设)总是设为零。
4、从托管堆分配对象时,可能强制执行一次垃圾回收。
如果所有类型都是引用类型,应用程序的性能将会显著下降。设想每次使用Int32值时都进行一次内存分配,性能会受到多么大的影响,为了提升简单和常用的类型的性能,CLR提供了名为‘值类型’的轻量级类型,值类型的实例一般在线程栈上分配,在代表值类型实例的变量中不包含指向实例的指针。相反,变量中包含了实例本身的字段。由于变量已经包含了实例的字段。因此,值类型的使用缓解了托管堆的压力,并减少了应用程序生存期内的垃圾回收次数。
文档清楚指出哪些是值类型,哪些是引用类型。在文档中查看类型时,任何成为‘类’的类型都是引用类型。例如:System.Exception类,System.IO.FileStream类以及System.Random类都是引用类型。相反所有的值类型都成为结构或枚举。例如:System.Int32结构、System.Boolean结构、System.Decimal结构、System.TimeSpan结构、System.DayOfWeek枚举等等。
进一步研究文档,会发现所有的结构都是抽象类型System.ValueType的直接派生类。System.ValueType本身又直接从System.Object派生。根据定义,所有值类型都必须从System.ValueType派生。CLR和所有编程语言都给予枚举特殊待遇。
虽然不能在定义值类型时为他选择基类型,但如果愿意,值类型可以实现一个或多个接口。除此之外,所有值类型都隐式密封,目的是防止将值类型用作其他引用类型或这类型的基类。例如:无法将Boolean、Char、Int32、Uint64、Single、Double、Decimal等类型来定义任何新类型。
以下代码演示了引用类型和值类型的区别:
//引用类型(因为是class)
class SomeRef
{
public Int32 x;
} //值类型(因为是struct)
struct SomeVal
{
public Int32 x;
} static void ValueTypeDemo()
{
SomeRef r1=new SomeRef(); //往堆上分配
SomeVal v1 = new SomeVal(); //往栈上分配 r1.x = 5; //提领指针
v1.x = 5; //在栈上修改
Console.WriteLine(r1.x); //显示为5
Console.WriteLine(v1.x); //同样显示为5 SomeRef r2 = r1; //只复用指针(引用)
SomeVal v2 = v1; //在栈上分配并复制成员
r1.x = 8; //r1.x和r2.x都会修改成新值
v1.x=9; //v1.x会修改,v2.x不会修改 Console.WriteLine(r1.x); //显示8
Console.WriteLine(r2.x); //显示8
Console.WriteLine(v1.x); //显示9
Console.WriteLine(v2.x); //显示5
}
上述代码中,SomeVal用struct声明,而不是用更常用的class。在C#中,常用struct声明的类型是值类型,用class声明的类型是引用类型。可以看出引用类型和值类型的区别相当大。再代码中使用类型时,必须注意是值类型还是引用类型,因为这会极大的影响在代码中表达自己意图的方式。
上述代码中有这样一行:
SomeVal v1 = new SomeVal(); //往栈上分配
因为这行代码的写法,似乎要在托管堆上分配一个SomeVal的实例。但c#编译器知道SomeVal是值类型,所以会生成正确的IL代码,在线程栈上分配一个SomeVal的实例。c#还会确保值类型中所有的字段都初始化为零。
SomeVal v1; //在栈上分配空间
这一行生成的IL代码也会在线程栈上分配实例,并将字段初始化为零。唯一的区别在于,如果使用new操作符,C#会认为实例已经初始化,以下代码更清楚的进行了说明:
//这两行代码都能够编译通过,因为c#认为v1的字段已经初始化为0
SomeVal v1 = new SomeVal();
Int32 a= v1.x; //这两行代码不能够编译通过,因为c#不认为v1的字段已经初始化为0
SomeVal v1;
Int32 a= v1.x; //error cs0170: 使用了可能未赋值的字段“x”;
设计自己的类型时,要仔细考虑清楚是否应该定义成值类型还是引用类型。值类型有时候能提供更好的性能。具体的说,除非满足一下全部条件,否则不应该声明为值类型。
1、类型具有基元类型的行为。也就是说是十分简单的类型,没有成员会修改类型的任何实例字段。
2、类型不需要从其他任何类型继承。
3、类型也不派生出其它任何类型。
NET基础(4):引用类型和值类型的更多相关文章
- C#基础知识-引用类型和值类型的区别(六)
在第一篇中我们介绍了C#中基本的15种数据类型,这15种数据类型中又分为两大类,一种是值类型,一种是引用类型.值类型有sbyte.short.long.int.byte.ushort.uint.ulo ...
- CLR via C#深解笔记三 - 基元类型、引用类型和值类型 | 类型和成员基础 | 常量和字段
编程语言的基元类型 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32(); // a = 0 a = 1 ...
- 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱
目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...
- C#基础(六)——值类型与引用类型
CLR支持两种类型:值类型和引用类型. 值类型包括C#的基本类型(用关键字int.char.float等来声明),结构(用struct关键字声明的类型),枚举(用enum关键字声明的类型):而引用类型 ...
- .NET 基础 一步步 一幕幕[面向对象之堆、栈、引用类型、值类型]
堆.栈.引用类型.值类型 内存分为堆和栈(PS:还有一种是静态存储区域 [内存分为这三种]),值类型的数据存储在栈中,引用类型的数据存储在堆中. 堆.栈: 堆和栈的区别: 栈是编译期间就分配好的内存空 ...
- 【C#进阶系列】05 基元类型、引用类型和值类型
基元类型和FCL类型 FCL类型就是指Int32这种类型,这是CLR支持的类型. 而基元类型就是指int这种类型,这是C#编译器支持的,实际上在编译后,还是会被转为Int32类型. 而且学过C的朋友 ...
- NET中的引用类型和值类型 zt
.NET中的类型分为值类型和引用类型,他们在内存布局,分配,相等性,赋值,存储以及一些其他的特性上有很多不同,这些不同将会直接影响到我们应用程序 的效率.本文视图对.NET 基础类型中的值类型和引用类 ...
- C# 引用类型和值类型
C# 引用类型和值类型 CLR支持两种类型:引用类型和值类型. 1.引用类型 (1)内存必须从托管堆上分配: (2)堆上分配的每个对象都有一些额外成员(包括“类型对象指针”,“同步块索引”),这些成员 ...
- 《CLR via C#》读书笔记--基元类型、引用类型和值类型
编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...
随机推荐
- 【转】C#中WinForm程序退出方法技巧总结
C#中WinForm程序退出方法技巧总结 一.关闭窗体 在c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.Ex ...
- C#对象序列化与反序列化
C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍.......................... ...
- LinQ和ADO.Net增删改查 备忘
是否些倦了 SqlConnection conn=new SqlConnection();一系列繁冗的代码? 来试试Linq吧 查: using System.Data.SqlClient; name ...
- 【Cocos2d-x游戏开发】Cocos2d-x中的弱联网技术
在上一篇博客中,我们一起学习了如何在Cocos2d-x中存储数据和读取信息,本篇博客我们将一起讨论和数据存储同样重要的联网技术. 一.弱联网技术介绍 在网络游戏中许多重要的功能都需要网络连接,而根据需 ...
- SASS+COMPASS 自适应 学习笔记
来源 http://snugug.github.io/RWD-with-Sass-Compass/#/ 1 安装 COMPASS 扩展 安装 方式 gem 'extension', '~>X.Y ...
- Qt写Activex插件 总结
最近写的插件功能基本完成,也遇到了一些坑,在这里记录一下. 我写的这个插件的js接口是仿造google earth的js接口,尽可能的达到与它的api一致.先从最简单的说起: 1. 导出接口中的flo ...
- 摘要: Linux下which、whereis、locate、find命令的区别
我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索.这些是从网上找到的资料,因为有时很长时间不会用到,当要用的时候经常弄混了,所以放到这里方便使用. which ...
- JavaScript获取当前日期,昨天,今天日期以及任意天数间隔日期
<script language="JavaScript" type="text/javascript"> function GetDateStr( ...
- CommonJS Promises/A规范
本文来自四火哥的翻译 CommonJS是一组javascript编程规范,而promise是其中之一. 简而言之,promises是一种令代码的异步行为变得更加优雅的软件抽象.在基本的定义中,代码可能 ...
- chart.js 里添加图表的清单:
chart.js 里添加图表的清单: var legend = myDoughnut.generateLegend(); $("#chart_legend").html(legen ...