c#编程
类比解释
继承的好处是什么?
- 它节省了大量的打字工作
- 它避免了代码的重复
通过类比解释继承
假设你想创建一只鹰、一只猎鹰和一只秃鹰。为了创建这些飞行生物,你会注意到每种生物都有相同的特征:
- 飞行
- 繁殖
- 吃饭
假设我们假设这三种鸟:鹰、猎鹰和秃鹰,飞行、繁殖和吃饭的方式完全相同。
如果没有继承,你将不得不重复代码。例如,控制鹰飞行的代码将会被复制到秃鹰的飞行功能中。对于程序员来说,这是个常识——我们不喜欢重复代码——重复代码几乎总是一个坏主意。
但注意,鹰、猎鹰和秃鹰实际上都是鸟。因此,你可以说鸟类一般具有吃、繁殖和飞行的特征。所以,使用“继承”你可以创建一个通用的“鸟”类原型,该原型包含飞行、繁殖和吃饭的功能。然后,一旦这个原型被定义,你可以让其他所有特定品种的鸟类继承这些特征。换句话说,使用这个原型,你可以在它的基础上设计出其他特定种类的鸟类。
这意味着,猎鹰会自动知道如何飞行,因为它继承了来自通用“鸟”类的行为。你基本上不需要重复代码。
继承
继承是从另一个类(“父类”)创建一个新类的能力,扩展父类的功能和状态到派生类(或“子类”)。它允许派生类重载其父类的方法。
继承是面向对象编程的支柱之一。它是一种从另一个类设计新类的机制,并且是代码重用的一个概念,支持层级分类的概念。C# 程序由类组成,其中新类可以从头开始创建,或者通过使用现有类的一部分或全部属性来创建。
与继承和代码重用相关的另一个特性是多态,它允许相同的方法名在不同的数据类型上执行不同的操作。因此,C# 通过这两种特性来支持代码的重用。
继承的主要特性包括:
- 派生类扩展其基类。也就是说,它包含父类的方法和数据,同时也可以包含自己特有的数据成员和方法。
- 派生类不能更改继承成员的定义。
- 构造函数和析构函数不被继承,其他基类成员会被继承。
- 派生类中成员的可访问性取决于其在基类中声明的可访问性。
- 派生类可以重写继承来的成员。
继承示例:
using System;
using System.Text;
namespace ContainmentInheritance
{
class Room
{
public int length;
public int width;
public int height;
public Room(int l, int w, int h)
{
length = l;
width = w;
height = h;
}
}
class Home
{
int numberOfRooms;
int plotSize;
string locality;
string name;
// 在 Home 类中创建一个 Room 类的对象
Room studyRoom = new Room(10, 12, 12);
public Home()
{
numberOfRooms = 1;
plotSize = 1000;
locality = "Versova";
name = "study room";
}
public void Display()
{
Console.WriteLine("MyHome has {0} rooms", numberOfRooms);
Console.WriteLine("Plot size is {0}", plotSize);
Console.WriteLine("Locality is {0}", locality);
int area = studyRoom.length * studyRoom.width;
Console.WriteLine("Area of the {0} room is {1}", name, area);
}
}
class Program
{
static void Main(string[] args)
{
Home myhome = new Home();
myhome.Display();
Console.ReadLine();
}
}
}
在这个示例中:
Room
类表示房间,它包含长度、宽度和高度等属性。Home
类表示一个房子,它包含一个Room
对象studyRoom
,并通过构造函数来初始化其属性。Display
方法用于展示房子的相关信息,包括房间的面积。
总结
继承使得你可以创建类的层次结构,避免了重复代码,并且使得不同类之间能够共享相同的行为和属性。
子类型继承
子类型继承指的是派生类继承父类的属性和方法,并可以重写(override)父类的某些方法以实现不同的功能。在C#中,使用virtual
关键字声明可以被重写的方法,使用override
关键字在派生类中实现新逻辑。
示例:Employee 类和 Executive 类
在以下代码示例中,我们展示了一个基本的员工类 Employee
和一个继承自 Employee
的执行者类 Executive
,执行者类重写了 GetPayCheck
方法,并添加了一个额外的方法 AdministerEmployee
。
public class Employee
{
// 声明一个方法为虚方法,以便子类 Executive 可以重写它。
public virtual void GetPayCheck()
{
// 获取薪水的逻辑
Console.WriteLine("Employee paycheck logic.");
}
// 员工和执行者都会工作,所以这里不需要虚方法
public void Work()
{
// 工作逻辑
Console.WriteLine("Employee working.");
}
}
public class Executive : Employee
{
// 使用 override 关键字表示我们想要为 GetPayCheck 方法提供新的实现
public override void GetPayCheck()
{
// 执行者的获取薪水逻辑
Console.WriteLine("Executive paycheck logic.");
}
// 实现额外的方法
public void AdministerEmployee()
{
// 管理员工的逻辑
Console.WriteLine("Executive administering employee.");
}
}
public class Program
{
static void Main(string[] args)
{
Employee emp = new Employee();
Executive exec = new Executive();
emp.Work(); // Employee 工作
exec.Work(); // Executive 继承 Employee 的 Work 方法
emp.GetPayCheck(); // Employee 的薪水获取逻辑
exec.GetPayCheck(); // Executive 的薪水获取逻辑
exec.AdministerEmployee(); // Executive 的管理员工逻辑
}
}
代码解析:
Employee
类:它包含两个方法,一个是GetPayCheck
(虚方法),另一个是Work
(普通方法)。GetPayCheck
方法被声明为virtual
,允许派生类重写它。Work
方法不需要重写,因为Employee
和Executive
都可以使用这个方法。Executive
类:它继承自Employee
,并使用override
关键字重写了GetPayCheck
方法。它还添加了一个新的方法AdministerEmployee
,该方法只在Executive
类中存在。
在 Main
方法中,创建了 Employee
和 Executive
对象,调用了他们的 Work
和 GetPayCheck
方法,演示了继承和重写的行为。
虚方法与多态性
当基类包含一个虚方法,并且派生类重写了该方法时,基类在调用该方法时,会实际调用派生类的重写版本。这是面向对象编程中多态性的一种表现。
示例:虚方法与多态
在这个示例中,基类 Resource
定义了一个虚方法 Close
,并在 Dispose
方法中调用它。如果派生类 AnotherTypeOfResource
重写了 Close
方法,当基类的 Dispose
方法被调用时,实际调用的是派生类的 Close
方法。
public class Resource : IDisposable
{
private bool _isClosed = false; // 良好的编程实践,初始化变量,尽管默认值也可以
protected virtual void Close()
{
Console.WriteLine("Base resource closer called!");
}
~Resource()
{
Dispose();
}
public void Dispose()
{
if (!_isClosed)
{
Console.WriteLine("Disposing resource and calling the Close() method...");
_isClosed = true;
Close();
}
}
}
public class AnotherTypeOfResource : Resource
{
// 重写基类的 Close 方法
protected override void Close()
{
Console.WriteLine("Another type of resource closer called!");
}
}
public class VirtualMethodDemo
{
public static void Main()
{
Resource res = new Resource();
AnotherTypeOfResource res2 = new AnotherTypeOfResource();
res.Dispose(); // 调用 Resource.Close()
res2.Dispose(); // 即使 Dispose() 是 Resource 类的一部分,
// 它会调用 AnotherTypeOfResource.Close()!
}
}
代码解析:
Resource
类:包含一个虚方法Close
,并在Dispose
方法中调用它。Dispose
方法用于释放资源并调用Close
方法进行清理。AnotherTypeOfResource
类:重写了Close
方法,改变了Close
的行为。Dispose
方法:尽管Dispose
是Resource
类的一部分,但由于Close
方法是虚方法,并且在派生类中被重写,实际调用的是派生类中的Close
方法。
总结
- 继承允许子类重用父类的代码,并可以根据需要重写(override)父类的方法以提供不同的实现。
- **虚方法(virtual method)**使得父类的方法可以被子类重写,提供多态性支持。父类调用虚方法时,会实际调用子类中重写后的方法。
构造函数
派生类不会自动继承基类的构造函数,除非它提供自己的构造函数。在实例化派生类时,必须通过使用 base
关键字调用基类的构造函数。
示例:
public class MyBaseClass
{
public MyBaseClass(string text)
{
Console.WriteLine(text);
}
}
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass(int number)
: base(number.ToString()) // 调用基类的构造函数
{ }
public MyDerivedClass(string text) // 即使与 MyBaseClass 的构造函数相同,仍然需要定义
: base(text) // 调用基类的构造函数
{ }
}
在上述示例中,MyDerivedClass
类提供了两个构造函数,并且通过 base
关键字调用了 MyBaseClass
的构造函数。即使 MyDerivedClass
的构造函数参数与基类的构造函数相同,它们也不能直接继承父类的构造函数,因此需要显式地调用父类构造函数。
继承关键字
在 C# 中,使用 :
操作符来表示从另一个类继承。
示例:
public class Executive : Employee
这表示 Executive
类继承自 Employee
类。
如果要标记一个方法为可以被重写的,使用 virtual
关键字。
示例:
public virtual void Write(string text)
{
System.Console.WriteLine("Text: {0}", text);
}
要重写一个方法,使用 override
关键字:
示例:
public override void Write(string text)
{
System.Console.WriteLine(text);
}
如果派生类的方法缺少 new
或 override
关键字,可能会导致编译时的错误或警告。
示例:
abstract class ShapesA
{
abstract public int Area(); // 抽象方法!
}
class Square : ShapesA
{
int x, y;
public int Area() // 错误:缺少 'override' 或 'new'
{
return x * y;
}
}
class Shapes
{
virtual public int Area() { return 0; } // 现在是虚方法!
}
class Square : Shapes
{
int x, y;
public int Area() // 不需要显式的 'override' 或 'new'
{
return x * y;
}
}
编译错误与警告
-
从抽象类继承时:
如果
Square
类从ShapesA
类继承并且没有使用override
关键字来重写Area
方法,编译时会产生错误:error CS0534: 'ConsoleApplication3.Square' does not implement inherited abstract member 'ConsoleApplication3.Shapes.Area()'
-
从虚方法类继承时:
如果
Square
类从Shapes
类继承且没有使用override
关键字来重写Area
方法,则会产生编译警告:warning CS0114: 'ConsoleApplication3.Square.Area()' hides inherited member 'ConsoleApplication3.Shapes.Area()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
这是因为
Area
方法在基类Shapes
中是虚方法(virtual
),而Square
类中的Area
方法没有明确使用override
或new
关键字。如果希望Square
类中的Area
方法重写基类的方法,必须加上override
关键字;如果不想覆盖基类的方法,可以使用new
关键字来隐藏它。