3.类型、存储和变量

目录

3.1 C#程序是一组类型声明

 3.2 类型是一种模板

 3.3 实例化类型

 3.4 数据成员和函数成员

成员的类别

3.5 预定义类型

预定义类型补充

 3.6 用户定义类型

3.7 栈和堆

3.7.1 栈

栈的特征

 3.7.2 堆

3.8 值类型和引用类型

3.8.1 存储引用类型对象的成员

3.8.2 C#类型的分类

 3.9 变量

 3.9.1 变量声明

1.变量初始化语句

2. 自动初始化

​编辑 3.9.2 多变量声明

3.9.3 使用变量的值

 3.10 静态类型和dynamic关键字

3.11 可空类型


3.1 C#程序是一组类型声明

如果广泛的描述C和C++程序源代码的特征,可以说C程序是一组函数和数据类型,C++程序是一组函数和类,然而C#程序是一组类型声明。

  • C#程序或DLL的源代码是一组一种或者多种类型声明。
  • 对于可执行程序,类型声明中必须有一个包含Main方法的类。
  • 命名空间是一种把相关的类型声明分组并命名的方法。既然程序是一组相关的类型声明,那么通常会吧程序声明在你创建的命名空间内部。

列如,下面是一个由3个类型声明组成的程序。这3个类型被声明在一个名称为MyProgram的新命名空间内部。

namespace MyProgram                 //声明新的命名空间
{

  DeclarationOfTypeA               //声明类型
  DeclarationOfTypeB              //声明类型


class C
 {

static void Main()
  {
  }
 }
}

 3.2 类型是一种模板

既然C#程序就是一组类型声明,那么学习C#就是学习如何创建和使用类型。所以,需要做的第一件事情就是了解什么类型。

可以把类型想象成一个用来创建数据结构的模板。模板本身并不是数据结构,但它详细说明了由该模板构造的对象的特征。

类型由下面的元素定义:

  • 名称;
  • 用于保存数据成员的数据结构;
  • 一些行为以及约束条件。

例如,图3-1阐述了short类型和int类型的组成元素。

3.类型、存储和变量_第1张图片

 3.3 实例化类型

从某个类型模板创建实际的对象,被称为实例化该类型。

  • 通过实例化类型而创建的对象被称为类型的对象或类型的示例。这两个术语可以互换。
  • 在C#程序中,每个数据项都是某种类型的实例。这些类型可以是语言自带的,可以是BCL或者其他库提供的,也可以是程序员定义的。

图3-2阐述了两种预定义类型对象的实例化。

3.类型、存储和变量_第2张图片

 3.4 数据成员和函数成员

像short、int和long等这样的类型称为简单类型。这种类型只能存储一个数据项。

其他的类型可以存储多个数据项。比如数组(array)类型就可以存储多个同类型的数据项。

这些数据项被称为数组元素。可以通过数字来引用这些元素,这些数字称为索引。数组将会在第12章讲述。

成员的类别

 然而另外一些类型可以包含许多不同类型的数据项。这些类型中的数据项个体被称为成员,并且与数组中使用数字来引用成员不同,这些成员具有独特的名称。

有两种成员:数据成员和函数成员。

  • 数据成员 保存了与这个类的对象或作为一个整体的类相关的数据。
  • 函数成员 执行代码。函数成员定义类型的行为。

例如,图3-3列出了类型XYZ的一些数据成员和函数成员,它包括两个数据成员和两个函数成员。

3.类型、存储和变量_第3张图片

3.5 预定义类型

C#提供了16种预定义类型,如图3-4所示。他们列在表3-1和表3-2中,其中包括13种简单类型和3种非简单类型。

所有定义类型的名称都由全小写的字母组成。预定义的简单类型包括以下3种。

  • 11种数值类型。
  1. 不同长度的有符号和无符号整数类型。
  2. 浮点数类型float和double。
  3. 一种称为decimal的高精度小数类型。与float和double不同,decimal类型可以准确地表示分数。decima类型通常用于货币计算。
  • 一种Unicode字符类型char。
  • 一种布尔类型bool.bool类型表示布尔值必须为true或false。

3种简单类型如下。

  • string 他是一个Unicode字符数组。
  • object 他是所有其他类型的基类。
  • dynamic 使用动态语言编写的程序集时使用。 

3.类型、存储和变量_第4张图片

预定义类型补充

所有预定义类型都直接映射到底层的.N3.ET类型。C#的类型名称就是.NET类型的别名,所以使用.NET的类型名称也能很好的符合C#的语法,不过并不鼓励这样做。在C#程序中,应该尽量使用C#类型名称而不是.NET类型名称。

预定义简单类型表示一个单一的数据项。表3-1列出了这些类型,并同时列出了他们的取值范围和对应底层.NET类型。

3.类型、存储和变量_第5张图片 

3.类型、存储和变量_第6张图片 

 3.6 用户定义类型

除了C#提供的16种预定义类型,还可以创建自己的用户定义类型。由6种类型可以由用户自己创建,他们是:

  • 类结构(clas);
  • 结构类型(struct);
  • 数组类型(array);
  • 枚举类型(enum);
  • 委托类型(delegate);
  • 接口类型(interface)。

类型通过类型声明创建,类型声明包含以下信息:

  • 要创建的类型的种类;
  • 新类型的名称;
  • 对类型中每个成员的声明(名称和规格),array和delegate类型除外,他们不含有命名成员。

一旦声明了类型,就可以创建和使用这种类型的对象,就像他们是预定义类型一样。图3-5概括了预定义类型和用户类型的使用。使用预定义类型是一个单步过程,简单地实例化对象即可。使用用户定义类型是一个两步过程:必须先声明类型,然后实例化该类型的对象。

3.类型、存储和变量_第7张图片

3.7 栈和堆

程序运行时,它的数据必须存储在内存中。一个数据项需要多大内存、存储在什么地方、以及如何存储都依赖于该数据项的类型。

运行中的程序使用两个内存区域来存储:栈和堆。 

3.7.1 栈

栈是一个内存数组,是一个LIFO(Last-In First-Out,先进先出)的数据结构。栈存储几种类型的数据:

  • 某些类型变量的值;
  • 程序当前的执行环境;
  • 传递给方法的参数。

系统管理所有的栈的操作。作为程序员,你不需要显示的队它做任何事情。但了解栈的基本功能可以更好的了解程序在运行时正在做什么,并能更好地了解C#文档和著作。

栈的特征

栈有如下几个普遍特征(见图3-6)。

  • 数据只能从栈的顶端插入和删除。
  • 把数据放到栈顶称为入栈(push)。
  • 把栈顶删除数据称为出栈(pop)。3.

3.类型、存储和变量_第8张图片

 3.7.2 堆

堆是一块内存区域,在堆里可以分配大块的内存用于存储某些类型的数据对象。与栈不同,堆里的内存能够以任意顺序进入和移除。图3-7展示了一个在堆里放了4项数据的程序。

3.类型、存储和变量_第9张图片

虽然程序可以在堆里保存数据,但并不能显示地删除它们。CLR的自动GC(Garbage Collector,垃圾收集器)在判断出程序的代码将不会在访问某项数据时,自动清除无主的堆对象。我们因此可以不再操心这项使用其他编程语言时非常容易出错的工作了。图3-8阐明了垃圾收集过程。

3.类型、存储和变量_第10张图片 

3.8 值类型和引用类型

 数据项的类定义了存储数据需要的内存大小以及组成该类型的数据成员。类型还决定了对象在内存中的存储位置------栈或堆。

类型被分为两种:值类型和引用类型,这两种类型的对象在内存中的存储方式不同。

  • 值类型只需要一段单独的内存,用于存储实际的数据。
  • 引用类型需要两段内存。
  1. 第一段存储实际数据,他总是位于堆中。
  2. 第二段是一个引用,指向数据在堆中的内存地址。

图3-9展示了每种类型的单个数据项是如何存储的。对于值类型,数据存放在栈里。对于引用类型,实际数据存放在堆里而引用存放在栈里。 

3.类型、存储和变量_第11张图片

3.8.1 存储引用类型对象的成员

图3-9阐述了当数据不是另一个对象的成员时如何存储。如果他是另一个对象成员,那么他的存储会有些不同。 

  • 引用类型对象的数据部分始终存储在堆里,如图3-9所示。
  • 值类型的对象,或引用类型的数据引用部分可以存放在堆里,也可以存放在栈里,这里依赖与实际环境。

例如,假设有一个引用类型的实例,名称为MyType,他有两个成员:一个值类型成员和一个引用类型成员。它将如何存储呢?是否是值类型成员存储在栈里,而引用类型存储在栈和堆之间分成两半呢?答案是否定的。

请记住,对于一个引用类型,其实例的数据部分始终存放在堆里。既然两个成员都是对象数据的一部分,那么它们都会被放在堆里,无论它们是值类型还是引用类型。图3-10解释了MyType的情形。

  • 尽管成员A是值类型,但它也是MyType实例数据的一部分,因此和对象的数据一起被存放在堆里。
  • 成员B是引用类型,所以他的数据部分存储在堆里,正如图中“数据”框所示。不同的是,它的引用部分也被存在堆里,封装在MyType对象的数据部分。

3.类型、存储和变量_第12张图片

 

3.8.2 C#类型的分类

 表3-3列出了C#中可以使用的所有类型以及它们的类别:值类型或引用类型。每种引用类型都将在后面的内容中阐述。 

3.类型、存储和变量_第13张图片

 3.9 变量

一种多用途的编程语言必须允许程序存储数据,而这正是通过变量实现的。

  • 变量是一个名称,表示程序在执行时存储在内存中的数据。
  • C#提供了4种变量,每一种都将详细讨论。表3-4列了变量的种类。

3.类型、存储和变量_第14张图片

 3.9.1 变量声明

变量在使用之前必须声明。变量声明定义了变量,完成两件事:

  • 给变量命名,并为他关联一种数据类型;
  • 让编译器为它分配一块内存。

一个简单的变量声明至少需要一个类型和一个名称。下面的声明定义了名称为var2的变量,类型为int:

3.类型、存储和变量_第15张图片

 3.类型、存储和变量_第16张图片

1.变量初始化语句

除声明变量的名称和类型以外,声明还能把它的内存初始化为一个明确的值。

变量初始化语句(variable initializer)由一个等括号后面跟一个初始值组成,如:

 无论初始化语句的本地变量有一个未定义的值,在未赋值之前不能使用。试图使用未定义的本地变量会导致编译器产生一条错误信息。

图3-12在左边展示了许多本地变量声明,在右边展示了栈的构造结果。一些变量有初始化语句,其他的变量没有。

3.类型、存储和变量_第17张图片

2. 自动初始化

一些类型的变量如果在声明时没有初始化语句,那么会被自动设置为默认值,而另一些则不能。没有自动初始化为默认值的变量在程序为它赋值之前包含未定义的值。表3-5展示了那种类型的变量会被自动初始化以及那种类型的变量不会被初始化。我会在以后的内容中对5中变量类型进行详细阐述。3.类型、存储和变量_第18张图片 

3.类型、存储和变量_第19张图片 3.9.2 多变量声明

可以把多个变量声明在一条单独的声明语句中。

  • 多变量声明的变量必须类型相同;
  • 变量名必须用逗号分隔,可以在变量名后包含初始化语句。

例如,下面的代码展示了两条有效的多变量声明语句。注意,只要使用逗号分开,初始化的变量可以和未来初始化的变量混在一起。最后一条声明语句是有问题的,因为他企图在一条语句中声明两个类型不同的变量。

3.类型、存储和变量_第20张图片

3.9.3 使用变量的值

变量名代表该变量保存的值,可以通过使用变量名来使用值。

例如,在如下语句中,变量名var2表示变量所存储的值。当语句执行的时候,会从内存中获取该值。 

 3.10 静态类型和dynamic关键字

你可能已经注意到了,每一个变量都包括变量类型。这样编译器就可以确定运行时需要的内存总量以及那些部分应该在栈上,哪些部分应该在堆上。变量的类型在编译的时候确定并且不能在运行时修改。这叫做静态类型。

但是不是所有语言都是静态类型的,诸如Ironpython和IronRuby之类的脚本语言是动态类型的。也就是说,变量的类型直到运行时才会被解析。由于他们是.NET语言,所以C#程序需要能够使用这些语言写的程序集。问题是,程序集中的类型到运行时才会被解析,而C#又要引用这样的类型并且需要在编译的时候解析类型。

针对这个问题,C#语言的设计者为语言增加了dynamic关键字,代表一个特定的、实际的C#类型,他知道如何在运行时解析自身。

在编译时,编译器不会对dynamic类型的变量进行类型检查。相反,它将与该变量以及该变量的操作有关的所有信息打包。在运行时,会对这些信息进行检查,以确保与变量所代表实际类型保持一致性,否则,将在运行时抛出异常。

3.11 可空类型

在某些情况下,特别是使用数据库的时候,你希望表示变量目前未保存有效的值。对于引用类型,这很简单,可以把变量设置为null。但定义值类型的变量时,不管它的内容是否有效的意义,其内存都会进行分配。

对于这种情况,你可能会使用一个布尔指示器来和变量关联,如果值有效,则设置为true,否则就设置为false。

可空类型允许创建可以标记为有效或无效的值类型,这样就可以在使用他之前确定值的有效性。普通的值类型称作为非空类型。我将在25章详细介绍可空类型,那时你已经对C#有了更好的了解。

 

 

你可能感兴趣的:(C#,c#,c++,开发语言)