C#中类的继承
成员方法
在 C# 的类的继承关系中,成员方法是可以被继承的, 除非该方法被标记为 private。这是面向对象编程中的一个基本特性,允许子类继承父类的行为。C# 的继承机制使得子类可以利用父类已有的功能,并根据需要进行扩展或修改,提高代码的复用性和可维护性。继承、覆盖和隐藏是实现多态的关键机制之一。
- 子类可以重写 (override) 继承的 virtual 或 abstract 方法,提供自己的实现。
- 子类可以隐藏 (new) 继承的方法,提供新的实现,但不会改变基类方法的行为。
拓展:
-
公有方法(public):可以被任何地方访问,包括其他类、子类及外部的调用。这些方法在子类中是可见的,除非它们被子类覆盖(override)或隐藏(new)。
-
保护方法(protected):只能在定义它的类内部、子类中或同一程序集的其他类中访问。这些方法在子类中是可见的,可以被子类直接使用或覆盖。
-
内部方法(internal):只在同一程序集中可见。对于子类,如果它们位于同一程序集中,这些方法可以被继承和访问。
-
受保护的内部方法(protected internal):可以被同一程序集中的任何类或任何派生类访问。这意味着这些方法对于子类是可继承的。
-
私有方法(private):只能在定义它的类内部访问。私有方法不会被子类继承,子类也无法直接访问这些方法。
-
私有保护的方法(private protected):可以被同一程序集中的派生类访问。这些方法在子类中是可继承的,但只限于在同一程序集中的子类。
析构函数
在 C# 中,析构函数不会被继承。析构函数实际上是一个语法糖,会被编译器转换成名为 Finalize() 的方法。Finalize() 方法会在垃圾回收器(GC)回收对象之前自动调用,用于清理非托管资源。子类不会继承父类的析构函数。每个类都需要定义自己的析构函数来清理自己的非托管资源。
拓展:
非托管资源指的是不由 .NET 运行时的垃圾回收器(Garbage Collector, GC)直接管理的资源。这些资源通常是操作系统级的资源,包括但不限于:
-
文件句柄:当你打开一个文件进行读写时,操作系统提供一个文件句柄来标识和管理这个文件。这个句柄是一个指向文件的指针或索引,由操作系统管理,而不是垃圾回收器。
-
窗口句柄(Window Handles, HWND):在基于 Windows 的应用程序中,每个窗口或对话框都有一个窗口句柄(HWND)。这些句柄是由 Windows 操作系统管理的,用于标识界面元素。
-
网络连接:如 TCP/UDP 套接字连接。这些连接由操作系统通过网络接口管理,.NET 提供了封装,但底层的套接字是非托管资源。
-
数据库连接:虽然 .NET 提供了数据库连接的高级管理,数据库连接本身经常涉及到底层的套接字和其他协议细节,这些都是非托管资源。
-
内存指针:如果你使用 C# 的 unsafe 关键字或调用某些 API(如 P/Invoke 调用 Win32 API 或调用 C/C++ 的 DLL),你可能直接操作指针。这些指针指向的内存不是由 .NET 的垃圾回收器管理的。
-
图形设备接口(GDI)对象:例如,字体、位图、画笔、画布等都是 GDI 对象。这些对象由 Windows GDI 系统管理,不是由 .NET 的垃圾回收器直接管理的。
-
COM 对象:组件对象模型(COM)是 Microsoft 的一个接口标准,用于允许不同的组件和应用程序之间进行交互。虽然 .NET 提供了与 COM 对象交互的能力(例如通过 RCW,Runtime Callable Wrapper),但这些 COM 对象本身是非托管的。
构造方法
在 C# 中,构造方法(构造函数)不会被继承。这是因为构造方法的目的是初始化创建类的新实例的状态。每个类都必须定义自己的构造方法,如果不显式定义,编译器会自动提供一个无参数的默认构造方法。
- 构造方法的名称必须与类名完全相同,类名不同构造方法不同。
- 构造方法的主要目的是初始化对象的状态,即设置成员变量和属性的初始值。
- 构造方法是与类实例化相关的,因此它不能被继承。每个类需要自己的构造方法来创建和初始化其对象。
拓展:
在构造一个派生类的对象时,构造方法的调用顺序是从基类到派生类。先调用基类的构造方法,然后是派生类的构造方法。
成员字段
在 C# 中,成员字段可以被继承,除非它们被声明为 private。成员字段的继承行为是面向对象编程中的一个基本特性,允许子类继承父类的状态和行为。
拓展:
-
公有字段(public):可以被任何地方访问,包括其他类、子类及外部的代码。这些字段在子类中是可见的,并且可以直接被子类对象访问。
-
保护字段(protected):只能在定义它的类内部、子类中或同一程序集的其他类中访问。这些字段在子类中是可见的,并可以被子类直接访问或修改。
-
内部字段(internal):只在同一程序集中可见。对于子类,如果它们位于同一程序集中,这些字段可以被继承和访问。
-
受保护的内部字段(protected internal):可以被同一程序集中的任何类或任何派生类访问。这意味着这些字段对于子类是可继承的。
-
私有字段(private):只能在定义它的类内部访问。私有字段不会被子类继承,子类也无法直接访问这些字段。
-
私有保护的字段(private protected):可以被同一程序集中的派生类访问。这些字段在子类中是可继承的,但只限于在同一程序集中的子类。
如果子类定义了与基类同名的字段,这会导致基类的字段被隐藏(但不是覆盖)。这种情况下,可以使用 new 关键字来明确表示隐藏意图,尽管这通常被认为是不良的设计,因为它可以导致代码混淆和使用错误。
字段和属性
字段是真正存储数据的,属性是不存储数据的,可以看作是对字段操作的封装并暴露给外界使用,字段只存储数据,应当私有保证安全。
一般写法属性后面要跟get和set,而字段就像其他语言那样赋初值或直接以分号结尾,并且可以只写属性不写对应的字段,这时编译器会自动生成对应的字段。如果不写get和set,一般会认为书写的是字段,哪怕使用public修饰,也会认为是公共字段,并且不推荐这种写法。