类简介

引用类型

定义为 class 的类型是引用类型。 在运行时,如果声明引用类型的变量,此变量就会一直包含值 null,直到使用 new 运算符显式创建类实例,或直到为此变量分配已在其他位置创建的兼容类型,如下面的示例所示:

//Declaring an object of type MyClass.
MyClass mc = new MyClass();

//Declaring another object of the same type, assigning it the value of the first object.
MyClass mc2 = mc;

创建对象时,在该托管堆上为该特定对象分足够的内存,并且该变量仅保存对所述对象位置的引用。 对象使用的内存由 CLR 的自动内存管理功能(称为垃圾回收)回收。 有关垃圾回收的详细信息,请参阅自动内存管理和垃圾回收

声明类

使用后跟唯一标识符的 class 关键字可以声明类,如下例所示:

//[access modifier] - [class] - [identifier]
public class Customer
{
   // Fields, properties, methods and events go here...
}

可选访问修饰符位于 class 关键字前面。 class 类型的默认访问权限为 internal。 此例中使用的是 public,因此任何人都可创建此类的实例。 类的名称遵循 class 关键字。 类名称必须是有效的 C# 标识符名称。 定义的其余部分是类的主体,其中定义了行为和数据。 类上的字段、属性、方法和事件统称为类成员

创建对象

虽然它们有时可以互换使用,但类和对象是不同的概念。 类定义对象类型,但不是对象本身。 对象是基于类的具体实体,有时称为类的实例。

可通过使用 new 关键字,后跟类的名称来创建对象,如下所示:

Customer object1 = new Customer();

创建类的实例后,会将一个该对象的引用传递回程序员。 在上一示例中,object1 是对基于 Customer 的对象的引用。 该引用指向新对象,但不包含对象数据本身。 事实上,可以创建对象引用,而完全无需创建对象本身:

Customer object2;

不建议创建不引用对象的对象引用,因为尝试通过这类引用访问对象会在运行时失败。 但是,但实际上引用可以引用某个对象,方法是创建新对象,或者将其分配给现有对象,例如:

Customer object3 = new Customer();
Customer object4 = object3;

此代码创建指向同一对象的两个对象引用。 因此,通过 object3 对对象做出的任何更改都会在后续使用 object4 时反映出来。 由于基于类的对象是通过引用来实现其引用的,因此类被称为引用类型。

构造函数和初始化

前面的部分介绍了声明类类型并创建该类型的实例的语法。 创建类型的实例时,需要确保其字段和属性已初始化为有用的值。 可通过多种方式初始化值:

  • 接受默认值
  • 字段初始化表达式
  • 构造函数参数
  • 对象初始值设定项

每个 .NET 类型都有一个默认值。 通常,对于数字类型,该值为 0,对于所有引用类型,该值为 null。 如果默认值在应用中是合理的,则可以依赖于该默认值。

当 .NET 默认值不是正确的值时,可以使用字段初始化表达式设置初始值:

public class Container
{
    // Initialize capacity field to a default value of 10:
    private int _capacity = 10;
}

可以通过定义负责设置初始值的构造函数来要求调用方提供初始值:

public class Container
{
    private int _capacity;

    public Container(int capacity) => _capacity = capacity;
}

从 C# 12 开始,可以将主构造函数定义为类声明的一部分:

public class Container(int capacity)
{
    private int _capacity = capacity;
}

向类名添加参数可定义主构造函数。 这些参数在包含其成员的类正文中可用。 可以将其用于初始化字段或需要它们的任何其他位置。

还可以对某个属性使用 required 修饰符,并允许调用方使用对象初始值设定项来设置该属性的初始值:

public class Person
{
    public required string LastName { get; set; }
    public required string FirstName { get; set; }
}

添加 required 关键字要求调用方必须将这些属性设置为 new 表达式的一部分:

var p1 = new Person(); // Error! Required properties not set
var p2 = new Person() { FirstName = "Grace", LastName = "Hopper" };

类继承

类完全支持继承,这是面向对象的编程的基本特点。 创建类时,可以从其他任何未定义为 sealed 的类继承。 其他类可以从你的类继承并替代类虚拟方法。 此外,你可以实现一个或多个接口。

继承是通过使用派生来完成的,这意味着类是通过使用其数据和行为所派生自的基类来声明的。 基类通过在派生的类名称后面追加冒号和基类名称来指定,如:

public class Manager : Employee
{
    // Employee fields, properties, methods and events are inherited
    // New Manager fields, properties, methods and events go here...
}

类声明包括基类时,它会继承基类除构造函数外的所有成员。 有关详细信息,请参阅继承

C# 中的类只能直接从基类继承。 但是,因为基类本身可能继承自其他类,因此类可能间接继承多个基类。 此外,类可以支持实现一个或多个接口。 有关详细信息,请参阅接口

类可以声明为 abstract。 抽象类包含抽象方法,抽象方法包含签名定义但不包含实现。 抽象类不能实例化。 只能通过可实现抽象方法的派生类来使用该类。 与此相反,密封类不允许其他类继承。 有关详细信息,请参阅抽象类、密封类和类成员

类定义可以在不同的源文件之间分割。 有关详细信息,请参阅分部类和方法

C# 语言规范

有关详细信息,请参阅 C# 语言规范。 该语言规范是 C# 语法和用法的权威资料。