C# 继承是一种重用机制,通过父子关系,一个类的成员可以被另一个类访问。C# 编程语言中继承的语法如下所示。
<Access modifier> Class <child class> : <parent class>
Class A // A is a parent class.
{
Class members
}
Class B: Class A // B is a child class
{
Members of A can be accessed here
}
父类也称为基类或超类,而子类也称为派生类或子类。
C# 继承示例
继承机制允许子类访问父类的成员(除了其私有成员),就像它是该成员的所有者一样。
要在 C# 中实现继承,父类构造函数必须对子类可访问。这意味着我们必须将父构造函数定义为 public,因为默认情况下,未定义任何修饰符的构造函数是 private 的。
让我们看一个实现继承的示例。
using System;
namespace CSharp_Tutorial
{
class Program
{
public Program()
{
Console.WriteLine("This is parent class constructor");
}
public void Test1()
{
Console.WriteLine("Test1 method");
}
public void Test2()
{
Console.WriteLine("Test2 method");
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
namespace CSharp_Tutorial
{
class Program1:Program
{
public Program1()
{
Console.WriteLine("This is child class constructor");
}
void Test3()
{
Console.WriteLine("Test3 method");
}
static void Main()
{
Program1 p = new Program1();
p.Test1(); p.Test2(); p.Test3();
Console.ReadLine();
}
}
}

在这个继承示例中,Program 是一个父类,其中创建了一个构造函数以及两个方法,Test1() 和 Test2()。接下来,Program1 也是一个包含构造函数和方法 Test3() 的类。但在 cProgram1 中,Program 的成员也被继承了。
现在,当我们尝试执行 Program1 时,编译器首先调用子类中的构造函数。同时,子构造函数隐式调用父构造函数以初始化父变量,以便子类可以使用父成员。
因此,C# 继承的执行顺序是父类构造函数,然后是子构造函数,接着是子类继承的方法和在其自身中定义的方法。
注意:如果父类也有一个父类,为子类创建的实例将调用其构造函数。同时,其构造函数将调用此父类的父构造函数,依此类推。执行从顶层的父类开始,向下到子类。
请记住,父类不能访问子类自身纯粹定义的子成员。
在父类中创建子类的引用
C# 父类不能访问子成员,但我们可以在继承中在父类中创建子类的引用。
我们之前已经了解到,引用只是实例的指针变量,即引用没有分配任何内存。但是,它指向实例的内存。即使我们可以创建子类的引用,使用该父类引用,我们也不能访问子类中纯粹定义的子成员。
using System;
namespace CSharp_Tutorial
{
class Program
{
public Program()
{
Console.WriteLine("This is parent class constructor");
}
public void Test1()
{
Console.WriteLine("Test1 method");
}
public void Test2()
{
Console.WriteLine("Test2 method");
}
static void Main()
{
Program p;
Program1 p1 = new Program1();
p = p1;
p.Test1(); p.Test2();
Console.ReadLine();
}
}
}

这里,p 是 C# 继承程序的一个局部变量,它是一个父类。接下来,p 用 Program1 的实例 p1 初始化,Program1 是一个子类。
现在 p 是一个未初始化且未分配任何内存的父变量,而 p1 是一个子实例。显然,实例总是会分配内存。
p 用 p1 初始化。这意味着 p 是实例 p1 的引用,通过它我们正在访问父类成员,但无法访问纯粹的子成员。
最后,我们可以说,在不为父类创建实例的情况下,我们可以使用指向子实例的引用来访问父成员。
对象类位于父层次结构的顶部
库中或我们自己定义的每个类都将有一个父类,即 System 命名空间中的 Object。
Object 类的成员是 Equals、GetHashCode、GetType、ToString。
因此,Object 的所有成员都可以从任何地方访问。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program1:Program
{
void Test3()
{
Console.WriteLine("Test3 method");
}
static void Main()
{
Object o = new Object();
Console.WriteLine(o.GetType());
Program p = new Program();
Console.WriteLine(p.GetType());
Program1 p1 = new Program1();
Console.WriteLine(p1.GetType());
Console.ReadLine();
}
}
}

GetType 是一个 Object 成员,可以从任何类访问。GetType 将获取特定实例的完全限定名。
这里,o 是 System 命名空间中对象的一个实例。这意味着 System.Object。接下来,p 和 p1 分别是 ConsoleApp3 命名空间中 Program 和 Program1 的实例,因此
ConsoleApp3.Program
ConsoleApp3.Program1
C# 中的继承类型
根据子类拥有的直接父类的数量或父类拥有的直接子类的数量,继承有两种类型。
- 单继承
- 多继承
如果一个类有一个直接父类,那么它属于单继承。相反,一个类有多个直接父类则属于多继承。然而,C# 只支持单继承,不支持类的多继承。
父类构造函数是带参数的构造函数吗?
正如我们已经知道的,当创建子类实例时,子构造函数会隐式调用父构造函数。这只发生在父构造函数是无参数的情况下。
但是,如果 C# 父类构造函数带参数,子构造函数就不能调用父类的构造函数。为了克服这个问题,我们应该从子类构造函数中显式调用它,并通过使用 base 关键字传递参数。
using System;
namespace CSharp_Tutorial
{
class Program
{
public Program(int a)
{
Console.WriteLine("Constructor of class Program is called: " + a);
}
public void Test1()
{
Console.WriteLine("Test1 method");
}
public void Test2()
{
Console.WriteLine("Test2 method");
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program1:Program
{
public Program1(int b):base(b)
{
Console.WriteLine("Constructor of class Program1 is called");
}
void Test3()
{
Console.WriteLine("Test3 method");
}
static void Main()
{
Program1 p1 = new Program1(10);
Console.ReadLine();
}
}
}

在子类 Program1 中,关键字 base 代表父类。
当实例 p1 创建时,它会调用 Program1 构造函数,然后通过传递参数 b(值为 10)调用 Program 构造函数。
这里,变量 b 也可以在子类中访问。
在实时环境中实现 C# 继承
在实时环境中,我们会遇到“实体”这个词,它不过是一个对象,以及关于该对象收集的数据。实体总是与特定属性相关联。
应用程序可以通过在不同实体之间建立关系来创建。例如,如果我们需要为学校开发一个应用程序,
学生、教学人员、非教学人员和临时人员通常是实体。
为每个实体维护一些常见属性。

ID、姓名、地址和电话是所有实体的共同属性。因此,通过这四个实体,我们可以创建一个名为 Person 的类,并使其成为所有这些实体的父类。
同样,对于员工,薪水和职位将是属性。Staff 可以用作教学和非教学人员的父类。
上图描述了在开发学校应用程序时如何实现继承。