多态性(C# 编程指南)Polymorphism (C# Programming Guide)

多态性常被视为自封装和继承之后,面向对象的编程的第三个支柱。Polymorphism is often referred to as the third pillar of object-oriented programming, after encapsulation and inheritance. Polymorphism(多态性)是一个希腊词,指“多种形态”,多态性具有两个截然不同的方面:Polymorphism is a Greek word that means "many-shaped" and it has two distinct aspects:

  • 在运行时,在方法参数和集合或数组等位置,派生类的对象可以作为基类的对象处理。At run time, objects of a derived class may be treated as objects of a base class in places such as method parameters and collections or arrays. 发生此情况时,该对象的声明类型不再与运行时类型相同。When this occurs, the object's declared type is no longer identical to its run-time type.

  • 基类可以定义并实现方法,派生类可以重写这些方法,即派生类提供自己的定义和实现 。Base classes may define and implement virtual methods, and derived classes can override them, which means they provide their own definition and implementation. 在运行时,客户端代码调用该方法,CLR 查找对象的运行时类型,并调用虚方法的重写方法。At run-time, when client code calls the method, the CLR looks up the run-time type of the object, and invokes that override of the virtual method. 因此,你可以在源代码中调用基类的方法,但执行该方法的派生类版本。Thus in your source code you can call a method on a base class, and cause a derived class's version of the method to be executed.

虚方法允许你以统一方式处理多组相关的对象。Virtual methods enable you to work with groups of related objects in a uniform way. 例如,假定你有一个绘图应用程序,允许用户在绘图图面上创建各种形状。For example, suppose you have a drawing application that enables a user to create various kinds of shapes on a drawing surface. 你在编译时不知道用户将创建哪些特定类型的形状。You do not know at compile time which specific types of shapes the user will create. 但应用程序必须跟踪创建的所有类型的形状,并且必须更新这些形状以响应用户鼠标操作。However, the application has to keep track of all the various types of shapes that are created, and it has to update them in response to user mouse actions. 你可以使用多态性通过两个基本步骤解决这一问题:You can use polymorphism to solve this problem in two basic steps:

  1. 创建一个类层次结构,其中每个特定形状类均派生自一个公共基类。Create a class hierarchy in which each specific shape class derives from a common base class.

  2. 使用虚方法通过对基类方法的单个调用来调用任何派生类上的相应方法。Use a virtual method to invoke the appropriate method on any derived class through a single call to the base class method.

首先,创建一个名为 Rectangle``Shape 的基类,并创建一些派生类,例如 Triangle``Circle、 和 。First, create a base class called Shape, and derived classes such as Rectangle, Circle, and Triangle. Shape 类提供一个名为 Draw 的虚拟方法,并在每个派生类中重写该方法以绘制该类表示的特定形状。Give the Shape class a virtual method called Draw, and override it in each derived class to draw the particular shape that the class represents. 创建一个 List<Shape> 对象,并向该对象添加 Circle、Triangle 和 Rectangle。Create a List<Shape> object and add a Circle, Triangle and Rectangle to it. 若要更新绘图图面,请使用 foreach 循环对该列表进行循环访问,并对其中的每个 Shape 对象调用 Draw 方法。To update the drawing surface, use a foreach loop to iterate through the list and call the Draw method on each Shape object in the list. 虽然列表中的每个对象都具有声明类型 Shape,但调用的将是运行时类型(该方法在每个派生类中的重写版本)。Even though each object in the list has a declared type of Shape, it is the run-time type (the overridden version of the method in each derived class) that will be invoked.

using System;
using System.Collections.Generic;

public class Shape
{
    // A few example members
    public int X { get; private set; }
    public int Y { get; private set; }
    public int Height { get; set; }
    public int Width { get; set; }
   
    // Virtual method
    public virtual void Draw()
    {
        Console.WriteLine("Performing base class drawing tasks");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        // Code to draw a circle...
        Console.WriteLine("Drawing a circle");
        base.Draw();
    }
}
class Rectangle : Shape
{
    public override void Draw()
    {
        // Code to draw a rectangle...
        Console.WriteLine("Drawing a rectangle");
        base.Draw();
    }
}
class Triangle : Shape
{
    public override void Draw()
    {
        // Code to draw a triangle...
        Console.WriteLine("Drawing a triangle");
        base.Draw();
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Polymorphism at work #1: a Rectangle, Triangle and Circle
        // can all be used whereever a Shape is expected. No cast is
        // required because an implicit conversion exists from a derived 
        // class to its base class.
        var shapes = new List<Shape>
        {
            new Rectangle(),
            new Triangle(),
            new Circle()
        };

        // Polymorphism at work #2: the virtual method Draw is
        // invoked on each of the derived classes, not the base class.
        foreach (var shape in shapes)
        {
            shape.Draw();
        }

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

}

/* Output:
    Drawing a rectangle
    Performing base class drawing tasks
    Drawing a triangle
    Performing base class drawing tasks
    Drawing a circle
    Performing base class drawing tasks
 */

在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 ObjectIn C#, every type is polymorphic because all types, including user-defined types, inherit from Object.

多态性概述Polymorphism Overview

虚成员Virtual Members

当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。When a derived class inherits from a base class, it gains all the methods, fields, properties and events of the base class. 派生类的设计器可以选择是否The designer of the derived class can choose whether to

  • 重写基类中的虚拟成员。override virtual members in the base class,

  • 继承最接近的基类方法而不重写它inherit the closest base class method without overriding it

  • 定义隐藏基类实现的成员的新非虚实现define new non-virtual implementation of those members that hide the base class implementations

仅当基类成员声明为 virtualabstract 时,派生类才能重写基类成员。A derived class can override a base class member only if the base class member is declared as virtual or abstract. 派生成员必须使用 override 关键字显式指示该方法将参与虚调用。The derived member must use the override keyword to explicitly indicate that the method is intended to participate in virtual invocation. 以下代码提供了一个示例:The following code provides an example:

public class BaseClass
{
    public virtual void DoWork() { }
    public virtual int WorkProperty
    {
        get { return 0; }
    }
}
public class DerivedClass : BaseClass
{
    public override void DoWork() { }
    public override int WorkProperty
    {
        get { return 0; }
    }
}

字段不能是虚拟的,只有方法、属性、事件和索引器才可以是虚拟的。Fields cannot be virtual; only methods, properties, events and indexers can be virtual. 当派生类重写某个虚拟成员时,即使该派生类的实例被当作基类的实例访问,也会调用该成员。When a derived class overrides a virtual member, that member is called even when an instance of that class is being accessed as an instance of the base class. 以下代码提供了一个示例:The following code provides an example:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Also calls the new method.

虚方法和属性允许派生类扩展基类,而无需使用方法的基类实现。Virtual methods and properties enable derived classes to extend a base class without needing to use the base class implementation of a method. 有关详细信息,请参阅使用 Override 和 New 关键字进行版本控制For more information, see Versioning with the Override and New Keywords. 接口提供另一种方式来定义将实现留给派生类的方法或方法集。An interface provides another way to define a method or set of methods whose implementation is left to derived classes. 有关详细信息,请参阅接口For more information, see Interfaces.

使用新成员隐藏基类成员Hiding Base Class Members with New Members

如果希望派生成员具有与基类中的成员相同的名称,但又不希望派生成员参与虚调用,则可以使用 new 关键字。If you want your derived member to have the same name as a member in a base class, but you do not want it to participate in virtual invocation, you can use the new keyword. new 关键字放置在要替换的类成员的返回类型之前。The new keyword is put before the return type of a class member that is being replaced. 以下代码提供了一个示例:The following code provides an example:

public class BaseClass
{
    public void DoWork() { WorkField++; }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { WorkField++; }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

通过将派生类的实例强制转换为基类的实例,仍然可以从客户端代码访问隐藏的基类成员。Hidden base class members can still be accessed from client code by casting the instance of the derived class to an instance of the base class. 例如:For example:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

阻止派生类重写虚拟成员Preventing Derived Classes from Overriding Virtual Members

无论在虚拟成员和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员永远都是虚拟的。Virtual members remain virtual indefinitely, regardless of how many classes have been declared between the virtual member and the class that originally declared it. 如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。If class A declares a virtual member, and class B derives from A, and class C derives from B, class C inherits the virtual member, and has the option to override it, regardless of whether class B declared an override for that member. 以下代码提供了一个示例:The following code provides an example:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

派生类可以通过将重写声明为 sealed 来停止虚拟继承。A derived class can stop virtual inheritance by declaring an override as sealed. 这需要在类成员声明中的 override``sealed 关键字前面放置 关键字。This requires putting the sealed keyword before the override keyword in the class member declaration. 以下代码提供了一个示例:The following code provides an example:

public class C : B
{
    public sealed override void DoWork() { }
}

在上一示例中,方法 DoWork 对从 C 派生的任何类都不再是虚方法。它对 C 的实例仍是虚拟的,即使它们转换为类型 B 或类型 A。使用 new 关键字可以将密封方法替换为派生类,如下方示例所示:In the previous example, the method DoWork is no longer virtual to any class derived from C. It is still virtual for instances of C, even if they are cast to type B or type A. Sealed methods can be replaced by derived classes by using the new keyword, as the following example shows:

public class D : C
{
    public new void DoWork() { }
}

在此情况下,如果在 D 中使用类型为 D 的变量调用 DoWork,被调用的将是新的 DoWorkIn this case, if DoWork is called on D using a variable of type D, the new DoWork is called. 如果使用类型为 C、B 或 A 的变量访问 D 的实例,对 DoWork``DoWork 的调用将遵循虚拟继承的规则,即把这些调用传送到类 C 的 实现。If a variable of type C, B, or A is used to access an instance of D, a call to DoWork will follow the rules of virtual inheritance, routing those calls to the implementation of DoWork on class C.

从派生类访问基类虚拟成员Accessing Base Class Virtual Members from Derived Classes

已替换或重写某个方法或属性的派生类仍然可以使用 base 关键字访问基类的该方法或属性。A derived class that has replaced or overridden a method or property can still access the method or property on the base class using the base keyword. 以下代码提供了一个示例:The following code provides an example:

public class Base
{
    public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
    public override void DoWork()
    {
        //Perform Derived's work here
        //...
        // Call DoWork on base class
        base.DoWork();
    }
}

有关详细信息,请参阅 baseFor more information, see base.

备注

建议虚拟成员在它们自己的实现中使用 base 来调用该成员的基类实现。It is recommended that virtual members use base to call the base class implementation of that member in their own implementation. 允许基类行为发生使得派生类能够集中精力实现特定于派生类的行为。Letting the base class behavior occur enables the derived class to concentrate on implementing behavior specific to the derived class. 未调用基类实现时,由派生类负责使它们的行为与基类的行为兼容。If the base class implementation is not called, it is up to the derived class to make their behavior compatible with the behavior of the base class.

本节内容In This Section

请参阅See also