继承(C# 编程指南)Inheritance (C# Programming Guide)

继承(以及封装和多形性)是面向对象的编程的三个主要特征之一。Inheritance, together with encapsulation and polymorphism, is one of the three primary characteristics of object-oriented programming. 通过继承,可以创建重用、扩展和修改在其他类中定义的行为的新类。Inheritance enables you to create new classes that reuse, extend, and modify the behavior that is defined in other classes. 其成员被继承的类称为“基类” ,继承这些成员的类称为“派生类” 。The class whose members are inherited is called the base class, and the class that inherits those members is called the derived class. 派生类只能有一个直接基类。A derived class can have only one direct base class. 但是,继承是可传递的。However, inheritance is transitive. 如果 ClassC 派生自 ClassB,并且 ClassB 派生自 ClassA,则 ClassC 会继承在 ClassB 和 ClassA 中声明的成员。If ClassC is derived from ClassB, and ClassB is derived from ClassA, ClassC inherits the members declared in ClassB and ClassA.

备注

结构不支持继承,但它们可以实现接口。Structs do not support inheritance, but they can implement interfaces. 有关详细信息,请参阅接口For more information, see Interfaces.

从概念上讲,派生类是基类的专门化。Conceptually, a derived class is a specialization of the base class. 例如,如果有一个基类 Animal,则可以有一个名为 Mammal 的派生类,以及另一个名为 Reptile 的派生类。For example, if you have a base class Animal, you might have one derived class that is named Mammal and another derived class that is named Reptile. MammalAnimalReptile 也是 Animal,但每个派生类表示基类的不同专门化。A Mammal is an Animal, and a Reptile is an Animal, but each derived class represents different specializations of the base class.

定义要从其他类派生的类时,派生类会隐式获得基类的所有成员(除了其构造函数和终结器)。When you define a class to derive from another class, the derived class implicitly gains all the members of the base class, except for its constructors and finalizers. 派生类因而可以重用基类中的代码,而无需重新实现。The derived class can thereby reuse the code in the base class without having to re-implement it. 在派生类中,可以添加更多成员。In the derived class, you can add more members. 通过这种方法,派生类可扩展基类的功能。In this manner, the derived class extends the functionality of the base class.

下图显示一个类 WorkItem,它表示某个业务流程中的工作项。The following illustration shows a class WorkItem that represents an item of work in some business process. 像所有类一样,它派生自 System.Object 且继承其所有方法。Like all classes, it derives from System.Object and inherits all its methods. WorkItem 添加了自己的五个成员。WorkItem adds five members of its own. 其中包括一个构造函数,因为无法继承构造函数。These include a constructor, because constructors are not inherited. ChangeRequest 继承自 WorkItem,表示特定类型的工作项。Class ChangeRequest inherits from WorkItem and represents a particular kind of work item. ChangeRequest 将另外两个成员添加到它从 WorkItemObject 继承的成员中。ChangeRequest adds two more members to the members that it inherits from WorkItem and from Object. 它必须添加自己的构造函数,并且还添加了 originalItemIDIt must add its own constructor, and it also adds originalItemID. 属性 originalItemID 使 ChangeRequest 实例可以与向其应用更改请求的原始 WorkItem 相关联。Property originalItemID enables the ChangeRequest instance to be associated with the original WorkItem to which the change request applies.

展示类继承的图

下面的示例演示如何在 C# 中表示前面图中所示的类关系。The following example shows how the class relationships demonstrated in the previous illustration are expressed in C#. 该示例还演示了 WorkItem 替代虚方法 Object.ToString 的方式,以及 ChangeRequest 类继承该方法的 WorkItem 的实现方式。The example also shows how WorkItem overrides the virtual method Object.ToString, and how the ChangeRequest class inherits the WorkItem implementation of the method.

// WorkItem implicitly inherits from the Object class.
public class WorkItem
{
    // Static field currentID stores the job ID of the last WorkItem that
    // has been created.
    private static int currentID;

    //Properties.
    protected int ID { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    protected TimeSpan jobLength { get; set; }

    // Default constructor. If a derived class does not invoke a base-
    // class constructor explicitly, the default constructor is called
    // implicitly. 
    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 constructor to initialize the static member, currentID. This
    // constructor is called one time, automatically, before any instance
    // of WorkItem or ChangeRequest is created, or currentID is referenced.
    static WorkItem()
    {
        currentID = 0;
    }


    protected int GetNextID()
    {
        // currentID is a static field. It is incremented each time a new
        // instance of WorkItem is created.
        return ++currentID;
    }

    // Method Update enables you to update the title and job length of an
    // existing WorkItem object.
    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()
    {
        return $"{this.ID} - {this.Title}";
    }
}

// ChangeRequest derives from WorkItem and adds a property (originalItemID) 
// and two constructors.
public class ChangeRequest : WorkItem
{
    protected int originalItemID { get; set; }

    // Constructors. Because neither constructor calls a base-class 
    // constructor explicitly, the default constructor in the base class
    // is called implicitly. The base class must contain a default 
    // constructor.

    // Default constructor for the derived class.
    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;

        // Property originalItemId is a member of ChangeRequest, but not 
        // of WorkItem.
        this.originalItemID = originalID;
    }
}

class Program
{
    static void Main()
    {
        // Create an instance of WorkItem by using the constructor in the 
        // base class that takes three arguments.
        WorkItem item = new WorkItem("Fix Bugs",
                                     "Fix all bugs in my code branch",
                                     new TimeSpan(3, 4, 0, 0));

        // Create an instance of ChangeRequest by using the constructor in
        // the derived class that takes four arguments.
        ChangeRequest change = new ChangeRequest("Change Base Class Design",
                                                 "Add members to the class",
                                                 new TimeSpan(4, 0, 0),
                                                 1);

        // Use the ToString method defined in WorkItem.
        Console.WriteLine(item.ToString());

        // Use the inherited Update method to change the title of the 
        // ChangeRequest object.
        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());

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    1 - Fix Bugs
    2 - Change the Design of the Base Class
*/

抽象方法和虚方法Abstract and Virtual Methods

基类将方法声明为虚拟 时,派生类可以使用其自己的实现重写该方法。When a base class declares a method as virtual, a derived class can override the method with its own implementation. 如果基类将成员声明为抽象,则必须在直接继承自该类的任何非抽象类中重写该方法。If a base class declares a member as abstract, that method must be overridden in any non-abstract class that directly inherits from that class. 如果派生类本身是抽象的,则它会继承抽象成员而不会实现它们。If a derived class is itself abstract, it inherits abstract members without implementing them. 抽象和虚拟成员是多形性(面向对象的编程的第二个主要特征)的基础。Abstract and virtual members are the basis for polymorphism, which is the second primary characteristic of object-oriented programming. 有关详细信息,请参阅多态性For more information, see Polymorphism.

抽象基类Abstract Base Classes

如果要通过使用 new 运算符来防止直接实例化,则可以将类声明为抽象You can declare a class as abstract if you want to prevent direct instantiation by using the new operator. 如果这样做,则仅当从该类派生新类时,才能使用该类。If you do this, the class can be used only if a new class is derived from it. 抽象类可以包含一个或多个本身声明为抽象的方法签名。An abstract class can contain one or more method signatures that themselves are declared as abstract. 这些签名指定参数和返回值,但没有任何实现(方法体)。These signatures specify the parameters and return value but have no implementation (method body). 抽象类不必包含抽象成员;但是,如果类包含抽象成员,则类本身必须声明为抽象。An abstract class does not have to contain abstract members; however, if a class does contain an abstract member, the class itself must be declared as abstract. 本身不抽象的派生类必须为来自抽象基类的任何抽象方法提供实现。Derived classes that are not abstract themselves must provide the implementation for any abstract methods from an abstract base class. 有关详细信息,请参阅抽象类、密封类和类成员For more information, see Abstract and Sealed Classes and Class Members.

接口Interfaces

接口 是引用类型,有些类似于仅包含抽象成员的抽象基类。An interface is a reference type that is somewhat similar to an abstract base class that consists of only abstract members. 类实现接口时,它必须为接口的所有成员提供实现。When a class implements an interface, it must provide an implementation for all the members of the interface. 类可以实现多个接口,即使它只能派生自单个直接基类。A class can implement multiple interfaces even though it can derive from only a single direct base class.

接口用于为类定义特定功能,这些功能不一定具有“是”关系。Interfaces are used to define specific capabilities for classes that do not necessarily have an "is a" relationship. 例如,System.IEquatable<T> 接口可由任何类或结构实现,这些类或构造必须启用客户端代码来确定该类型的两个对象是否等效(但是由该类型定义等效性)。For example, the System.IEquatable<T> interface can be implemented by any class or struct that has to enable client code to determine whether two objects of the type are equivalent (however the type defines equivalence). IEquatable<T> 不表示基类和派生类之间存在的同一种“是”关系(例如,MammalAnimal)。IEquatable<T> does not imply the same kind of "is a" relationship that exists between a base class and a derived class (for example, a Mammal is an Animal). 有关详细信息,请参阅接口For more information, see Interfaces.

防止进一步派生Preventing Further Derivation

类可以通过将自己或成员声明为密封,来防止其他类继承自它或继承自其任何成员。A class can prevent other classes from inheriting from it, or from any of its members, by declaring itself or the member as sealed. 有关详细信息,请参阅抽象类、密封类和类成员For more information, see Abstract and Sealed Classes and Class Members.

基类成员的派生类隐藏Derived Class Hiding of Base Class Members

派生类可以通过使用相同名称和签名声明成员来隐藏基类成员。A derived class can hide base class members by declaring members with the same name and signature. new 修饰符可以用于显式指示成员不应作为基类成员的重写。The new modifier can be used to explicitly indicate that the member is not intended to be an override of the base member. 使用 new 不是必需的,但如果未使用 new,则会生成编译器警告。The use of new is not required, but a compiler warning will be generated if new is not used. 有关详细信息,请参阅使用 Override 和 New 关键字进行版本控制了解何时使用 Override 和 New 关键字For more information, see Versioning with the Override and New Keywords and Knowing When to Use Override and New Keywords.

请参阅See also