c# 类型专题
类型
类型、变量和值
C# 是一种强类型语言。 每个变量和常量都有一个类型,每个计算为值的表达式也是如此。 每个方法签名为每个输入参数和返回值指定一个类型。 .NET Framework 类库定义了一组内置数值类型以及表示各种逻辑构造的更复杂的类型,例如文件系统、网络连接、对象的集合和数组及日期。 典型 C# 程序使用类库中的类型,还使用为特定于该程序问题域的概念建模的用户定义类型。
类型中存储的信息可以包括:
-
该类型的变量所需的存储空间。
-
该类型可以表示的最大值和最小值。
-
该类型包含的成员(方法、字段、事件等)。
-
该类型所继承的基类型。
-
将在运行时为其分配变量内存的位置。
-
允许的运算种类。
编译器使用类型信息确保代码中执行的所有运算都是类型安全的。 例如,如果声明了一个 int 类型的变量,则编译器允许您在加法和减法运算中使用此变量。 如果尝试在一个bool 类型的变量上执行相同的运算,则编译器会产生错误,如下面的示例所示:
int a = 5;
int b = a + 2; //OKbool test = true;
// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;编译器将类型信息作为元数据嵌入到可执行文件中。 公共语言运行时 (CLR) 会在运行时使用该元数据,以进一步确保它在分配和回收内存时类型安全。
通用类型系统
请务必了解有关 .NET Framework 中的类型系统的以下两个基本点:
-
它支持继承原则。 类型可从称为基类型的其他类型派生。 派生类型继承基类型的方法、属性和其他成员(存在一些限制)。 之后,基类型可从某些其他类型派生,这种情况下,派生类型继承其层次结构中这两个基类型的成员。 包括如 System.int32(C# 关键字:int)等内置数值类型在内的所有类型,最终都是从一个基类派生得到的,该基类即 System.object (C# 关键字:object)。 这种统一的类型层次结构称为常规类型系统 (CTS)。
CTS 中的每一个类型都被定义成了值类型或引用类型。 这包括 .NET Framework 类库中的所有自定义类型以及您自己的用户定义类型。 使用关键字 sturct 定义的类型是值类型;所有内置数值类型都是 structs。 使用关键字 class 定义的类型是引用类型。 引用类型和值类型有不同的编译时规则和不同的运行时行为。
值类型
-
-
值类型是从派生自System.Object 的 System.ValueType 派生的。 派生自System.ValueType 的类型在 CLR 中有特殊行为。 值类型变量直接包含它们的值,这意味着内存在声明变量的任意上下文中都是以内联方式分配的。 值类型变量没有单独的堆分配或垃圾回收开销。
值类型分为两个类别:struct 和enum。
内置数值类型是结构,它们具有可以访问的属性和方法。
值类型是密封的,这意味着您不能从 System.Int32 派生类型,并且不能定义一个结构以便从任何用户定义的类或结构继承,因为结构只能从 System.ValueType 继承。 但是,一个结构可以实现一个或更多个接口。 可以将结构类型强制转换为接口类型;但这会使装箱操作在托管堆上的一个引用类型对象内包装该结构。 在将值类型传递给将 System.Object 作为输入参数的方法时会发生装箱操作。
另一类值类型是 enum。 枚举定义一组已命名的整数常量。 例如,.NET Framework 类库中的 System.IO.FileMode 枚举包含一组已命名的整数常量,用来指定应如何打开文件。 其定义方式如下面的示例所示:
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}引用类型
定义为类、委托、数组或接口的类型是引用类型。 在运行时,当您声明引用类型的变量时,该变量会一直包含值 null,直至您使用 new 运算符显式创建对象的实例
接口必须与实现它的类对象一起初始化。 如果 MyClass 实现 IMyInterface,则您创建了 IMyInterface 的实例,如下面的示例所示:
IMyInterface iface = new MyClass();
创建对象时,将在托管堆上分配内存,变量只保存对对象位置的引用。 对于托管堆上的类型,在 CLR 的自动内存管理功能(称为“垃圾回收”)对它们进行分配和回收时都需要系统开销。 但是,也对垃圾回收进行了高度优化,在大多数情况下它不会引起性能问题。
所有数组都是引用类型,即使其元素是值类型也不例外。 数组是从 System.Array 类隐式派生的,但可以通过 C# 提供的简化语法来声明和使用它们,如下面的示例所示:
// Declare and initialize an array of integers.
int[] nums = { 1, 2, 3, 4, 5 };// Access an instance property of System.Array.
int len = nums.Length;泛型类型
一个类型可以通过一个或多个类型参数声明,而这些类型参数作为客户端代码在创建该类型的实例时提供的实际类型(具体类型)的占位符。 这种类型称为“泛型类型”。 例如,.NET Framework 类型 System.Collections.Generic.List<T> 有一个类型参数,按照约定该类型参数的名称为 T。 在创建该类型的实例时,会指定列表将包含的对象的类型,例如字符串:
List<string> strings = new List<string>();
-
使用类型参数便可以重复使用相同的类存放任意类型的元素,而不必将每个元素都转换为对象。 泛型集合类称为“强类型集合”,因为编译器知道集合中元素的特定类型,举例来说,如果尝试向上面示例中的 strings 对象添加整数,编译器会在编译时引发错误。