1.继承的介绍
(1).继承,连同封装和多态性,是面向对象编程的三个主要特征之一。继承使您能够创建新类,这些类重用、扩展和修改其他类中定义的行为。继承其成员的类称为基类,继承这些成员的类称为派生类。一个派生类只能有一个直接基类。但是,继承是可传递的。if 是从 派生而来的,并且是从 中派生的,则继承 在 和 中声明的成员。
例如:ClassC
ClassB
ClassB
ClassA
ClassC
ClassB
ClassA
结构不支持继承,但它们可以实现接口。
(2).从概念上讲,派生类是基类的特化。例如,如果您有一个基类,则可能有一个名为 的派生类和另一个名为 的派生类。A 是 一个 ,a 是一个 ,但每个派生类表示基类的不同特化。
Animal
Mammal
Reptile
Mammal
Animal
Reptile
Animal
(3).接口声明可以为其成员定义默认实现。这些实现由派生接口和实现这些接口的类继承。有关默认接口方法的详细信息,请参阅有关接口的文章。
(4).将一个类定义为从另一个类派生时,派生类将隐式获取基类的所有成员,但其构造函数和终结器除外。派生类重用基类中的代码,而不必重新实现它。可以在派生类中添加更多成员。派生类扩展了基类的功能。
(5).下图显示了一个类,该类表示某个业务流程中的工作项。像所有类一样,它派生自 System.Object 并继承其所有方法。 增加了五个自己的成员。这些成员包括构造函数,因为构造函数不会被继承。类继承自并表示特定类型的工作项。 向它从 Object 继承和从 Object 继承的成员再添加两个成员。它必须添加自己的构造函数,并且还要添加 。属性使实例能够与应用更改请求的原始实例相关联。
WorkItem
WorkItem
ChangeRequest
WorkItem
ChangeRequest
WorkItem
originalItemID
originalItemID
ChangeRequest
WorkItem
(6).
下面的示例演示如何在 C# 中表示上图中演示的类关系。该示例还演示如何重写虚拟方法 Object.ToString,以及该类如何继承该方法的实现。第一个块定义了类:WorkItem
ChangeRequest
WorkItem
public class WorkItem
{
private static int currentID;
protected int ID { get; set; }
protected string Title { get; set; }
protected string Description { get; set; }
protected TimeSpan jobLength { get; set; }
public WorkItem()
{
ID = 0;
Title = "Default title";
Description = "Default description.";
jobLength = new TimeSpan();
}
// Instance constructor that has three parameters.
public WorkItem(string title, string desc, TimeSpan joblen)
{
this.ID = GetNextID();
this.Title = title;
this.Description = desc;
this.jobLength = joblen;
}
static WorkItem() => currentID = 0;
protected int GetNextID() => ++currentID;
public void Update(string title, TimeSpan joblen)
{
this.Title = title;
this.jobLength = joblen;
}
// Virtual method override of the ToString method that is inherited
// from System.Object.
public override string ToString() =>
$"{this.ID} - {this.Title}";
}
public class ChangeRequest : WorkItem
{
protected int originalItemID { get; set; }
public ChangeRequest() { }
// Instance constructor that has four parameters.
public ChangeRequest(string title, string desc, TimeSpan jobLen,
int originalID)
{
// The following properties and the GetNexID method are inherited
// from WorkItem.
this.ID = GetNextID();
this.Title = title;
this.Description = desc;
this.jobLength = jobLen;
this.originalItemID = originalID;
}
}
下一个块显示如何使用基类和派生类:
WorkItem item = new WorkItem("Fix Bugs",
"Fix all bugs in my code branch",
new TimeSpan(3, 4, 0, 0));
ChangeRequest change = new ChangeRequest("Change Base Class Design",
"Add members to the class",
new TimeSpan(4, 0, 0),
1);
Console.WriteLine(item.ToString());
change.Update("Change the Design of the Base Class",
new TimeSpan(4, 0, 0));
// ChangeRequest inherits WorkItem's override of ToString.
Console.WriteLine(change.ToString());
/* Output:
1 - Fix Bugs
2 - Change the Design of the Base Class
*/
2.抽象和虚拟方法
当基类将方法声明为虚拟方法时,派生类可以使用其自己的实现重写该方法。如果基类将成员声明为抽象,则必须在直接从该类继承的任何非抽象类中重写该方法。如果派生类本身是抽象的,则它继承抽象成员而不实现它们。抽象和虚成员是多态性的基础,多态性是面向对象编程的第二个主要特征。有关详细信息,请参阅多态性。
3.抽象基类
如果要使用 new 运算符阻止直接实例化,则可以将类声明为抽象类。仅当从抽象类派生了新类时,才能使用抽象类。抽象类可以包含一个或多个方法签名,这些方法签名本身被声明为抽象签名。这些签名指定参数和返回值,但没有实现(方法主体)。抽象类不必包含抽象成员;但是,如果类确实包含抽象成员,则必须将类本身声明为抽象。本身不抽象的派生类必须为抽象基类中的任何抽象方法提供实现。
4.接口
口是定义一组成员的引用类型。实现该接口的所有类和结构都必须实现该组成员。接口可以为这些成员中的任何一个或所有成员定义默认实现。一个类可以实现多个接口,即使它只能从单个直接基类派生。
接口用于为不一定具有"is a"关系的类定义特定功能。例如,System.IEquatable<T> 接口可以由任何类或结构实现,以确定该类型的两个对象是否等效(但该类型定义了等效性)。IEquatable<T> 并不意味着基类和派生类之间存在的相同类型的"is a"关系(例如,a 是一个)。有关详细信息,请参阅接口。Mammal
Animal
5.基类成员的派生类隐藏
派生类可以通过声明具有相同名称和签名的成员来隐藏基类成员。新的修饰符可用于显式指示该成员不打算作为基成员的重写。不需要使用 new,但如果未使用 new,将生成编译器警告。有关详细信息,请参阅使用替代和新关键字进行版本控制和了解何时使用替代和新关键字。