C#/.NET值类型

  • 内容
  • 评论
  • 相关

值类型 (Value Type) 包括两个成员:结构体和枚举类型。

通常来说,值类型就是字面意义上的那种值,例如整数 int,小数 float/double,布尔值等。

而实际上,整数、小数,布尔值等全部都是结构体。

值类型的默认值一般为 0,例如整数和小数的默认值都是 0,枚举类型的默认值也为 0, char 的默认值为 ‘\0’。和引用类型相比,值类型的内存分配简单得多。

基元类型

之前讲过,C# 和其他 .NET 语言都是运行在通用类型系统(CTS)上的,而 CTS 提供 一些“基本的”类型一基元类型(Primitive Type)。

各个 .NET 语言分别使用不同的关键字, 但最终它们都会被映射到同一个 IL 类型。这样的类型就叫做基元类型,它们由 CTS 定义,由编译器与 BCL 直接支持,属于 BCL 而非任何某个语言。

基元类型包括了几乎所有的值类型(除了用户定义的结构体和枚举)以及字符串,object 和 dynamic。

Primitive 有原始的意思,可以将基元类型理解为基本的、原始的类型,少了它们就什么都做不了。

有了基元类型,各个 .NET 语言的互操作性就可以实现了,例如,通过 ildasm 工具,我们可以查看到 int i=1 对应的 IL 代码为(这里省略了赋值的那一句 IL代码):

.locals init ([0] int32 i)

这说明了在 IL 中 int 对应的基元类型为 Int32。当然,在 C# 中也可以直接写 Int32 i=1,不过,这样并不会给你带来任何好处。

而对于VB.NET,int 的关键字为 Integer,如果你在 VB.NET 中声明了一个Integer,你也可以通过 ildasm 发现,它对应的类型仍然为 Int32。

值类型的内存分配

值类型的内存分配分为以下几种情况:

  • 值类型作为局部变量。
  • 值类型作为引用类型的成员。
  • 值类型中包含引用类型。

1) 值类型作为局部变量

普通的值类型总是分配在栈上。例如以最简单的 int 为例,inti=1 意味着我们在栈上开辟了一块空间存储这个值类型。

注意,int 实际上是一个结构体,它有 2 个值类型成员(最大值,最小值),它们是常量,所以是静态的(const=static readonly)。

静态的成员和 int 的方法均存储在加载堆中。

值类型也没有同步块索引和类型对象指针。所以,新建一个 int,不会重新复制它的最大值和最小值,int 的开销永远是 4 个字节(就是它自己)。

即使机器是 64 位机,int 的大小永远是 32 位,因为 int 实质上是 Int32。Int64 这个基元类型在 C# 中对应 long。

对于局部变量的复制来说,情况非常简单。我们知道,值类型复制时,将只复制值的副本。所以更改原值对复制的新值不会有影响。

var i = 1;
var j = i;
i = 2 ;
//输出1
Console.WriteLine(j);

当执行代码var j = i时,将会在栈上新建一个名为 j 的变量,然后将 i 的值复制给 j,它和 i 没有任何关系。值类型也不可能有浅复制。

2) 值类型作为引用类型的成员

如果值类型为引用类型的成员,则遵从引用类型的内存分配和复制方式。例如:

public class AClass
{
    public int a;
    public string b;
}

在创建一个该类的实例时,遵从引用类型的内存分配方式。

下面的代码会实例化一个 AClass 对象:

var a = new AClass();
a. a = 1;
a.b = "hey";

执行完上面的代码之后,内存的分配如下图所示。

值类型作为引用类型的成员

本文标题:C#/.NET值类型

本文地址:https://www.hosteonscn.com/5005.html

评论

0条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注