检查 .NET 类型系统
C# 是强类型语言。 每个变量和常量都有一个类型,计算结果为值的每个表达式也一样。 .NET 类库定义表示各种构造的内置数值类型和复杂类型。 这些构造包括文件系统、网络连接、集合和对象数组以及日期。 典型的 C# 程序使用类库中的类型和用户定义的类型,这些类型对特定于程序问题域的概念进行建模。
内置类型
C# 提供一组标准的内置类型。 这些标准类型表示整数、浮点值、布尔表达式、文本字符、小数值和其他类型的数据。 还有内置字符串和对象类型。 这些类型可用于任何 C# 程序。
自定义类型
可以使用 struct、class、interface、enum和 record 构造来创建自己的自定义类型。 .NET 类库本身是可在自己的应用程序中使用的自定义类型的集合。 默认情况下,类库中最常用的类型在任何 C# 程序中都可用。 仅当显式添加对定义它们的程序集的项目引用时,其他人才可用。 编译器对程序集的引用后,可以在源代码中声明该程序集中声明的类型(和常量)。
通用类型系统
了解 .NET 中类型系统的两个基本点非常重要:
它支持继承原则。 类型可以派生自其他类型,称为基类型。 派生类型继承方法、属性和基类型的其他成员(有一些限制)。 基类型反过来可以派生自某些其他类型,在这种情况下,派生类型继承其继承层次结构中这两种基类型的成员。 所有类型(包括 System.Int32(C# 关键字:int)等内置数值类型最终派生自单个基类型,即
System.Object(C# 关键字:object)。 这种统一类型层次结构称为通用类型系统(CTS)。CTS 中的每个类型都定义为值类型或引用类型。 这些类型包括 .NET 类库中的所有自定义类型,以及你自己的用户定义的类型。 使用结构关键字定义的类型是值类型;所有内置数值类型都是结构。 使用类或记录关键字定义的类型是引用类型。 引用类型和值类型具有不同的编译时规则和不同的运行时行为。
下图显示了 CTS 中的值类型和引用类型之间的关系。
类和结构是 .NET 中通用类型系统的基本构造之二。 每个结构本质上都是一个数据结构,它封装一组作为逻辑单元属于一起的数据和行为。 数据和行为是 class、struct或 record的成员。 类的成员包括属性(数据)、方法(行为)、字段(类中声明的变量),等等。
class、struct或 record 声明类似于用于在运行时创建实例或对象的蓝图。 如果定义名为 Person的类,则 Person 类型的名称。 如果声明和初始化 p类型的变量 Person,则 p 据说是 Person的对象或实例。 可以创建同一 Person 类型的多个实例,并且每个实例在其属性和字段中可以具有不同的值。
类是引用类型。 创建类型的对象时,为其分配对象的变量只保留对该内存的引用。 将对象引用分配给新变量时,新变量引用原始对象。 通过一个变量所做的更改反映在另一个变量中,因为它们都引用了相同的数据。
struct 是值类型。 创建 struct 时,为其分配 struct 的变量将保留 struct的实际数据。 将 struct 分配给新变量时,将复制数据值。 因此,新变量和原始变量包含同一数据的两个单独的副本。 对一个副本所做的更改不会影响另一个副本。
记录类型可以是引用类型(record class)或值类型(record struct)。 记录类型包含支持值相等性的方法。
一般情况下,类用于对更复杂的行为进行建模。 类通常存储在创建类对象后要修改的数据。 结构最适合小型数据结构。 结构通常存储创建 struct 后不打算修改的数据。 记录类型是具有其他编译器合成成员的数据结构。 记录通常存储创建对象后不打算修改的数据。
值类型
值类型派生自 System.ValueType,派生自 System.Object。 从 System.ValueType 派生的类型在公共语言运行时中具有特殊行为。 值类型变量直接包含其值。
struct 的内存在声明变量的任何上下文中内联分配。 值类型变量没有单独的堆分配或垃圾回收开销。 可以声明属于值类型的记录结构类型,并包括记录的合成成员。
有两类值类型:结构与枚举。
内置数值类型是结构,它们具有可以访问的字段和方法:
// constant field on type byte.
byte b = byte.MaxValue;
但是,声明并为其赋值,就像它们是简单的非聚合类型一样:
byte num = 0xA;
int i = 5;
char c = 'Z';
值类型 密封。 不能从任何值类型派生类型,例如 System.Int32。 无法定义从任何用户定义的 struct 或 class 继承的 struct,因为 struct 只能从 System.ValueType继承。
使用结构关键字创建自己的自定义值类型。 通常,结构用作一小部分相关变量的容器,如以下示例所示:
public struct Coords
{
public int x, y;
public Coords(int p1, int p2)
{
x = p1;
y = p2;
}
}
另一类值类型是 enum。
enum 定义一组命名整型常量。 例如,.NET 类库中的 System.IO.FileMode 枚举包含一组命名的常量整数,用于指定应如何打开文件。 以下示例显示了 enum 的语法:
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
System.IO.FileMode.Create 常量的值为 2。 但是,对于读取源代码的用户来说,名称更有意义,因此最好使用枚举而不是常量文本数字。
所有 enums 都继承自 System.Enum,后者继承自 System.ValueType。 应用于 structs 的所有规则也适用于 enums。
引用类型
class、record、delegate、array和 interface 类型 引用类型。
声明引用类型的变量时,它将包含值 null,直到使用该类型的实例分配它或使用 new 运算符创建一个。
以下示例演示如何使用数组声明引用类型变量:
// Declaring an array variable
int[] numbers;
// Initializing the array with a size of 5
numbers = new int[5];
// Alternatively, declaring and initializing an array in one line
int[] numbers2 = new int[] { 1, 2, 3, 4, 5 };
// Assigning a reference to another variable
int[] numbers3 = numbers2;
new 运算符创建类型的实例,并返回对该实例的引用。 引用是对象的内存地址,该引用存储在变量中。 将引用类型变量分配给另一个变量时,将复制引用,而不是对象本身。 这两个变量都引用内存中的同一对象。
注意
除了引用类型外,数组也是集合。 可以使用集合表达式初始化集合,这消除了在一行中声明和初始化数组时包括 new 关键字的要求。 例如:int[] numbers = [ 1, 2, 3, 4, 5 ];。
可以使用用于实例化内置类型的相同语法创建类的实例。 以下示例演示如何创建类的实例:
MyClass myClass = new MyClass();
MyClass myClass2 = myClass;
new 运算符创建类的实例,并返回对该实例的引用。 引用是对象的内存地址,该引用存储在变量中。 将引用类型变量分配给另一个变量时,将复制引用,而不是对象本身。 这两个变量都引用内存中的同一对象。