阅读《CLR via C#》总结(第四天)

4 篇文章 0 订阅

ch5——基元类型、引用类型和值类型

基元类型:编译器直接支持的数据类型称为基元类型。

FCL类型:Framework类库中存在的类型。

C#基元类型与对应的FCL类型
C#基元类型FCL类型符合CLS说明
sbyteSystem.SByte有符号8位值
byteSystem.Byte无符号8位值
shortSystem.Int16有符号16位值
ushortSystem.UInt16无符号16位值
intSystem.Int32有符号32位值
uintSystem.UInt32无符号32位值
longSystem.Int64有符号64位值
ulongSystem.UInt64无符号64位值
charSystem.Char16位Unicode字符(char不像在非托管c++中那样代表一个8位值)
floatSystem.SingleIEEE32位浮点值
doubleSystem.DoubleIEEE64位浮点值
boolSystem.Booleantrue/false值
decimalSystem.Decimal128位高精度浮点值,常用于不容许舍入误差的金融计算。128位中,1位是符号,96位是值本身(N),8位是比例因子(k).decimal实际值是±N*10^k,其中-28<=k<=0。其余位没有使用。
stringSystem.String字符数组
objectSystem.Object所有类型的基类型
dynamicSystem.Object对于CLR,dynamic和object完全一致。但C#编译器允许使用简单的语法让dynamic变量参与动态调度。

引用类型和值类型 

 CLR支持两种类型:引用类型和值类型。引用类型总是从托管堆分配,C#的new操作符返回对象内存地址——即指向对象数据的内存地址。

使用引用类型必须注意以下四点:

  1. 内存必须从托管堆分配
  2. 堆上分配的每个对象都有一些额外成员,这些成员必须初始化。
  3. 对象的其他字节(为字段而设)总是设为零。
  4. 从托管堆分配对象时,可能强制执行一次垃圾回收。

值类型一般在线程栈上分配(虽然也可作为字段嵌入引用类型的对象中)。 在代表值类型实例的变量中不包含指向实例的指针,相反,变量中包含了实例本身的字段。值类型实例不受垃圾回收器的控制。

所有值类型从System.ValueType中派生,ValueType本身又从System.Object中派生。所有值类型都隐式密封,防止将值类型用作其他引用类型或值类型的基类型。

根据前一章的叙述可理解引用类型对象在堆上开辟空间,其包含类型对象指针和同步索引块以及一些自身的实例字段,其引用的地址会存储在栈上,值类型对象会在栈上开辟空间,那么当我们对一个引用类型做了复制操作(即类似于Object o1;Object o2=o1,则此时o2是o1的复制)会在栈中复制出一个新的对象对其引用类型在堆上的内存引用,此时我们对o1的更改的同时o2也会改动,因为二者此时引用的是同一个东西

相反,值类型对象复制后是两个不同的对象,二者之前不会有关联。

除非满足以下全部条件,否则不应将类型声明为值类型

  1. 类型具有基元类型的行为。(没有任何成员会修改类型的任何实例字段,即不可变类型,对于值类型,建议将全部字段标记为readonly)。
  2. 类型不需要从其他任何类型继承。
  3. 类型也不派生出其他任何类型。

因为实参默认以传值方式传递,返回也相同,都会对值类型实例中的字段进行复制,对性能造成损害,所以还必须满足以下条件

  1. 类型的实例较小(16字节或更小)
  2. 类型的实例较大(大于16字节) ,但不作为方法实参传递,也不从方法返回。

值类型变量赋值给另一个值类型变量,会执行逐字段的复制。引用类型变量赋值给另一个一引用类型的变量只复制内存地址。

值类型的装箱和拆箱 

 简单的说值类型到引用类型就是装箱,反之为拆箱。

那么我们来深入了解一下

struct Point{
    public Int32 x,y;
}
public sealed class Program{
    public static void Main(){
        ArrayList a = new ArrayList();
        Point p;
        for(Int32 i = 0; i<10;i++){
            p.x=p.y=i;
            a.Add(p);
        }
        ...
    }
}

在循环中可看到,add获取的是一个Object参数(获取托管堆上的一个对象引用或指针)来作为参数,但p是值类型,所以Point值类型在此必须转换成真正的,在堆中托管的对象,且必须获得该对象的引用。

装箱步骤:

  1. 在托管堆中分配内存,分配的内存是值类型各字段所需的内存量,还需加上所有托管堆对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量。
  2. 值类型字段复制到新分配的内存
  3. 返回对象地址。该地址为对象的引用。 

 拆箱并不是直接倒过来,代价会低很多。

eg: Point p =(Point) a[0];

我们通过此方式获取第一个元素的引用

  1. 获取已装箱Point对象的各个Point字段的地址(拆箱)
  2. 将字段包含的值从堆复制到栈上的值类型实例中。
public static void Main(){
    Point p;
    p.x=p.y=1;
    Object o =p;
    
    p=(Point)o;
    p.x=2;
    o=p;
}

由上述代码可直观的看出,后三行代码仅为更改p的x的值进行了拆箱再封箱的操作,其复制对应用程序性能的影响非常大。

 

public static Void Main(){
    Int32 v = 5;
    Object o = v;
    v = 123;
    
    Console.WriteLine(v+", "+(Int32)o);
}

可以看出上述代码进行过3次装箱么?第一次装箱在Object o =v;此时对v进行装箱将引用放到o中,第二和第三次在Console.WriteLine()中,v 此时进行装箱,将指针留在栈上以进行Concat(连接)操作。第三次在o进行拆箱后再次对Int32进行装箱,将指针保留在栈上进行Concat操作。

 上述代码可简化为:

public static Void Main(){
    Int32 v = 5;
    Object o = v;
    v = 123;
    
    Console.WriteLine(v.ToString()+", "+o);
}

由于v.ToString方法会返回一个String类型对象,以及o本身就是Object类型对象,可避免后两次装箱及o对Int32的拆箱操作。

如果知道自己代码将会造成编译器反复对一个值类型进行装箱,可以改成手动方式进行装箱

public static void Main(){
    Int32 v = 5;
    Console.WriteLine("{0},{1},{2}",v,v,v);//这里对v进行了3次装箱
    //上述代码完全可以更改为:
    Int32 v = 5;
    Object o = v;
    Console.WriteLine("{0},{1},{2}",o,o,o);
}
    

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值