了解何时使用 Override 和 New 关键字(C# 编程指南)

在 C# 中,派生类中方法的名称可与基类中方法的名称相同。 可通过使用 newoverride 关键字指定方法互动的方式。 override 修饰符 extends 基类方法,且 new 修饰符将其“隐藏”起来。 这种区别在本主题中的示例显示出来。

在控制台应用程序中,声明下面的 BaseClass 和 DerivedClass 两个类. DerivedClass 继承自 BaseClass。

class BaseClass
{
    public void Method1()
    {
        Console.WriteLine("Base - Method1");
    }
}

class DerivedClass : BaseClass
{
    public void Method2()
    {
        Console.WriteLine("Derived - Method2");
    }
}

在 Main 方法中,声明变量 bc、dc 和 bcdc。

  • bc 的类型为 BaseClass,并且其值的类型为 BaseClass。

  • dc的类型为 DerivedClass,并且其值的类型为 DerivedClass。

  • bcdc的类型为 BaseClass,并且其值的类型为 DerivedClass。 这是要密切注意的变量。

由于 bc 和 bcdc 具有类型 BaseClass,因此,除非您使用强制转换,否则它们只会直接访问 Method1。 变量 dc 可以访问 Method1 和 Method2。 下面的代码演示这些关系。

class Program
{
    static void Main(string[] args)
    {
        BaseClass bc = new BaseClass();
        DerivedClass dc = new DerivedClass();
        BaseClass bcdc = new DerivedClass();

        bc.Method1();
        dc.Method1();
        dc.Method2();
        bcdc.Method1();
    }
    // Output:
    // Base - Method1
    // Base - Method1
    // Derived - Method2
    // Base - Method1
}

接下来,将以下 Method2 方法添加到 BaseClass。 此方法的签名与 DerivedClass 中 Method2 方法的签名相匹配。

public void Method2()
{
    Console.WriteLine("Base - Method2");
}

由于 BaseClass 现在有 Method2 方法,因此可以为 BaseClass 变量 bc 和 bcdc 添加第二个调用语句,如下面的代码所示。

bc.Method1();
bc.Method2();
dc.Method1();
dc.Method2();
bcdc.Method1();
bcdc.Method2();

当生成项目时,您将看到在 BaseClass 中添加 Method2 方法将引发警告。 警告提示,DerivedClass 中的 Method2 方法将 Method2 方法隐藏在 BaseClass 中。 如果要获得该结果,则建议您使用 Method2 定义中的 new 关键字。 或者,可以重命名 Method2 方法之一来解决警告,但这始终不实用。

在添加 new 之前,运行该程序以查看其他调用语句生成的输出。 显示以下结果。

// Output:
// Base - Method1
// Base - Method2
// Base - Method1
// Derived - Method2
// Base - Method1
// Base - Method2

new 关键字可以保留生成输出的关系,但它将取消警告。 具有 BaseClass 类型的变量继续访问 BaseClass 成员,具有 DerivedClass 类型的变量首先继续访问 DerivedClass 中的成员,然后再考虑从 BaseClass 继承的成员.

要禁止显示警告,请向 DerivedClass 中的 Method2 定义添加 new 修饰符,如下面的示例所示: 可在 public 前后添加修饰符。

public new void Method2()
{
    Console.WriteLine("Derived - Method2");
}

再次运行该程序以确认没有更改输出。 还确认警告不再出现。 通过使用 new,您断言您了解它修改的成员将隐藏从基类继承的成员。 关于通过继承隐藏名称的更多信息,请参见 new 修饰符(C# 参考)

要将此行为与使用 override 的效果进行对比,请将以下方法添加到 DerivedClass。 可在 public 的前面或后面添加 override 修饰符。

public override void Method1()
{
    Console.WriteLine("Derived - Method1");
}

将 virtual 修饰符添加到 BaseClass 中的 Method1 的定义。 可在 public 的前面或后面添加 virtual 修饰符。

public virtual void Method1()
{
    Console.WriteLine("Base - Method1");
}

再次运行项目。 尤其请注意下面输出的最后两行。

// Output:
// Base - Method1
// Base - Method2
// Derived - Method1
// Derived - Method2
// Derived - Method1
// Base - Method2

使用 override 修饰符使 bcdc 能够访问 DerivedClass 中定义的 Method1 方法。 通常,这是继承层次结构中所需的行为。 让具有从派生类创建的值的对象使用派生类中定义的方法。 通过使用 override 扩展基类方法可实现该行为。

下面的代码包括完整的示例。

using System;
using System.Text;

namespace OverrideAndNew
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClass bc = new BaseClass();
            DerivedClass dc = new DerivedClass();
            BaseClass bcdc = new DerivedClass();

            // The following two calls do what you would expect. They call
            // the methods that are defined in BaseClass.
            bc.Method1();
            bc.Method2();
            // Output:
            // Base - Method1
            // Base - Method2


            // The following two calls do what you would expect. They call
            // the methods that are defined in DerivedClass.
            dc.Method1();
            dc.Method2();
            // Output:
            // Derived - Method1
            // Derived - Method2


            // The following two calls produce different results, depending 
            // on whether override (Method1) or new (Method2) is used.
            bcdc.Method1();
            bcdc.Method2();
            // Output:
            // Derived - Method1
            // Base - Method2
        }
    }

    class BaseClass
    {
        public virtual void Method1()
        {
            Console.WriteLine("Base - Method1");
        }

        public virtual void Method2()
        {
            Console.WriteLine("Base - Method2");
        }
    }

    class DerivedClass : BaseClass
    {
        public override void Method1()
        {
            Console.WriteLine("Derived - Method1");
        }

        public new void Method2()
        {
            Console.WriteLine("Derived - Method2");
        }
    }
}

以下示例显示了不同上下文中的类似行为。 该示例定义了三个类:一个名为 Car 的基类,和两个由其派生的 ConvertibleCar 和 Minivan。 基类中包含 DescribeCar 方法。 该方法给出了对一辆车的基本描述,然后调用 ShowDetails 来提供其他的信息。 这三个类中的每一个类都定义了 ShowDetails 方法。 new 修饰符用于定义 ConvertibleCar 类中的 ShowDetails。 override 修饰符用于定义 Minivan 类中的 ShowDetails。

// Define the base class, Car. The class defines two methods,
// DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived
// class also defines a ShowDetails method. The example tests which version of
// ShowDetails is selected, the base class method or the derived class method.
class Car
{
    public void DescribeCar()
    {
        System.Console.WriteLine("Four wheels and an engine.");
        ShowDetails();
    }

    public virtual void ShowDetails()
    {
        System.Console.WriteLine("Standard transportation.");
    }
}

// Define the derived classes.

// Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails
// hides the base class method.
class ConvertibleCar : Car
{
    public new void ShowDetails()
    {
        System.Console.WriteLine("A roof that opens up.");
    }
}

// Class Minivan uses the override modifier to specify that ShowDetails
// extends the base class method.
class Minivan : Car
{
    public override void ShowDetails()
    {
        System.Console.WriteLine("Carries seven people.");
    }
}

该示例测试被调用的 ShowDetails 版本。 以下方法,TestCars1 为每个类提供了一个实例,并在每个实例上调用 DescribeCar。

public static void TestCars1()
{
    System.Console.WriteLine("\nTestCars1");
    System.Console.WriteLine("----------");

    Car car1 = new Car();
    car1.DescribeCar();
    System.Console.WriteLine("----------");


    // Notice the output from this test case. The new modifier is
    // used in the definition of ShowDetails in the ConvertibleCar
    // class.  

    ConvertibleCar car2 = new ConvertibleCar();
    car2.DescribeCar();
    System.Console.WriteLine("----------");

    Minivan car3 = new Minivan();
    car3.DescribeCar();
    System.Console.WriteLine("----------");
}

TestCars1 生成以下输出: 尤其请注意 car2 的结果,该结果可能不是您所需的内容。 对象的类型是 ConvertibleCar,但 DescribeCar 不会访问 ConvertibleCar 中定义的 ShowDetails 版本,因为方法已声明包含 new 修饰符,而不是 override 修饰符。 因此,ConvertibleCar 对象显示与 Car 对象相同的说明。 比较 car3 的结果,它是一个 Minivan 对象。 在这种情况下,在 Minivan 类中声明的 ShowDetails 方法重写 Car 类中声明的 ShowDetails 方法,显示的说明描述微型面包车。

// TestCars1
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------

TestCars2 创建 Car 类型的对象列表。 对象的值由 Car、ConvertibleCar 和 Minivan 类实例化而来。 DescribeCar 是调用列表中的每个元素。 以下代码显示了 TestCars2 的定义。

public static void TestCars2()
{
    System.Console.WriteLine("\nTestCars2");
    System.Console.WriteLine("----------");

    var cars = new List<Car> { new Car(), new ConvertibleCar(), 
        new Minivan() };

    foreach (var car in cars)
    {
        car.DescribeCar();
        System.Console.WriteLine("----------");
    }
}

显示以下输出。 请注意,此输出与由 TestCars1 显示的输出相同。 ConvertibleCar 类的 ShowDetails 方法不被调用,无论对象的类型是 ConvertibleCar,如在 TestCars1 中,还是 Car,如在 TestCars2 中。 相反,car3 在两种情况下都从 Minivan 类调用 ShowDetails 方法,无论它具有类型 Minivan 还是类型 Car。

// TestCars2
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------

完成示例的方法 TestCars3 和 TestCars4。 这些方法直接调用 ShowDetails,首先从宣布具有类型 ConvertibleCar 和 Minivan (TestCars3) 的对象调用,然后从具有类型 Car (TestCars4) 的对象调用。 以下代码定义了这两种方法。

public static void TestCars3()
{
    System.Console.WriteLine("\nTestCars3");
    System.Console.WriteLine("----------");
    ConvertibleCar car2 = new ConvertibleCar();
    Minivan car3 = new Minivan();
    car2.ShowDetails();
    car3.ShowDetails();
}

public static void TestCars4()
{
    System.Console.WriteLine("\nTestCars4");
    System.Console.WriteLine("----------");
    Car car2 = new ConvertibleCar();
    Car car3 = new Minivan();
    car2.ShowDetails();
    car3.ShowDetails();
}

该方法产生下面的输出,它对应本主题中第一个示例的结果。

// TestCars3
// ----------
// A roof that opens up.
// Carries seven people.

// TestCars4
// ----------
// Standard transportation.
// Carries seven people.

以下代码显示了整个项目及其输出。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OverrideAndNew2
{
    class Program
    {
        static void Main(string[] args)
        {
            // Declare objects of the derived classes and test which version
            // of ShowDetails is run, base or derived.
            TestCars1();

            // Declare objects of the base class, instantiated with the
            // derived classes, and repeat the tests.
            TestCars2();

            // Declare objects of the derived classes and call ShowDetails
            // directly.
            TestCars3();

            // Declare objects of the base class, instantiated with the
            // derived classes, and repeat the tests.
            TestCars4();
        }

        public static void TestCars1()
        {
            System.Console.WriteLine("\nTestCars1");
            System.Console.WriteLine("----------");

            Car car1 = new Car();
            car1.DescribeCar();
            System.Console.WriteLine("----------");

            // Notice the output from this test case. The new modifier is
            // used in the definition of ShowDetails in the ConvertibleCar
            // class.  
            ConvertibleCar car2 = new ConvertibleCar();
            car2.DescribeCar();
            System.Console.WriteLine("----------");

            Minivan car3 = new Minivan();
            car3.DescribeCar();
            System.Console.WriteLine("----------");
        }
        // Output:
        // TestCars1
        // ----------
        // Four wheels and an engine.
        // Standard transportation.
        // ----------
        // Four wheels and an engine.
        // Standard transportation.
        // ----------
        // Four wheels and an engine.
        // Carries seven people.
        // ----------


        public static void TestCars2()
        {
            System.Console.WriteLine("\nTestCars2");
            System.Console.WriteLine("----------");

            var cars = new List<Car> { new Car(), new ConvertibleCar(), 
                new Minivan() };

            foreach (var car in cars)
            {
                car.DescribeCar();
                System.Console.WriteLine("----------");
            }
        }
        // Output:
        // TestCars2
        // ----------
        // Four wheels and an engine.
        // Standard transportation.
        // ----------
        // Four wheels and an engine.
        // Standard transportation.
        // ----------
        // Four wheels and an engine.
        // Carries seven people.
        // ----------


        public static void TestCars3()
        {
            System.Console.WriteLine("\nTestCars3");
            System.Console.WriteLine("----------");
            ConvertibleCar car2 = new ConvertibleCar();
            Minivan car3 = new Minivan();
            car2.ShowDetails();
            car3.ShowDetails();
        }
        // Output:
        // TestCars3
        // ----------
        // A roof that opens up.
        // Carries seven people.
 

        public static void TestCars4()
        {
            System.Console.WriteLine("\nTestCars4");
            System.Console.WriteLine("----------");
            Car car2 = new ConvertibleCar();
            Car car3 = new Minivan();
            car2.ShowDetails();
            car3.ShowDetails();
        }
        // Output:
        // TestCars4
        // ----------
        // Standard transportation.
        // Carries seven people.
    }


    // Define the base class, Car. The class defines two virtual methods,
    // DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each derived
    // class also defines a ShowDetails method. The example tests which version of
    // ShowDetails is used, the base class method or the derived class method.
    class Car
    {
        public virtual void DescribeCar()
        {
            System.Console.WriteLine("Four wheels and an engine.");
            ShowDetails();
        }

        public virtual void ShowDetails()
        {
            System.Console.WriteLine("Standard transportation.");
        }
    }


    // Define the derived classes.

    // Class ConvertibleCar uses the new modifier to acknowledge that ShowDetails
    // hides the base class method.
    class ConvertibleCar : Car
    {
        public new void ShowDetails()
        {
            System.Console.WriteLine("A roof that opens up.");
        }
    }

    // Class Minivan uses the override modifier to specify that ShowDetails
    // extends the base class method.
    class Minivan : Car
    {
        public override void ShowDetails()
        {
            System.Console.WriteLine("Carries seven people.");
        }
    }

}

请参见

参考

类和结构(C# 编程指南)

使用 Override 和 New 关键字进行版本控制(C# 编程指南)

base(C# 参考)

abstract(C# 参考)

概念

C# 编程指南