了解何时使用替代和新关键字 (C# 编程指南)

在 C# 中,派生类中的方法可以具有与基类中的方法相同的名称。 可以使用 新的 关键字和 重写 关键字来指定方法的交互方式。 修饰符 override扩展 基类 virtual 方法,修饰符 new隐藏 可访问的基类方法。 本主题中的示例说明了差异。

在控制台应用程序中,声明以下两个类, BaseClass 以及 DerivedClassDerivedClass 继承自 BaseClass.

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

在方法中Main,声明变量bcdcbcdc

  • bc 为类型 BaseClass,其值为类型 BaseClass

  • dc 为类型 DerivedClass,其值为类型 DerivedClass

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

bcbcdc 具有类型 BaseClass,因此它们只能直接访问 Method1,除非使用转换。 变量 dc 可以访问 Method1Method2。 这些关系显示在以下代码中。

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. 此方法的签名与Method2中的DerivedClass方法的签名匹配。

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

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

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

生成项目时,你会看到在Method2中添加BaseClass方法导致警告。 警告指出Method2中的DerivedClass方法隐藏了Method2中的BaseClass方法。 如果希望获得该结果,则建议使用 new 定义中的 Method2 关键字。 或者,可以重命名其中 Method2 一个方法来解决警告,但这并不总是可行的。

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

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

关键字 new 保留生成该输出的关系,但会禁止显示警告。 具有类型BaseClass的变量继续访问BaseClass的成员,具有类型DerivedClass的变量首先访问DerivedClass的成员,然后考虑从BaseClass继承的成员。

若要取消警告,请按以下代码所示,将new修饰符添加到Method2中的DerivedClass定义。 可以在public的前面或后面添加修饰符。

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

请再次运行程序以确认输出未发生变化。 另请验证警告是否不再出现。 通过使用 new,断言你知道它修饰的成员将隐藏从基类继承的成员。 有关通过继承隐藏名称的详细信息,请参阅 新的修饰符

若要将此行为与使用 override的效果形成对比,请将以下方法添加到 DerivedClass其中。 修饰符可以添加在overridepublic之前或之后。

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

virtual修饰符添加到Method1中的BaseClass定义中。 修饰符可以添加在virtualpublic之前或之后。

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

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

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

使用override修饰符可以让bcdc访问在Method1中定义的DerivedClass方法。 通常,这是继承层次结构中所需的行为。 你希望从派生类创建值的对象能够使用派生类中定义的方法。 通过使用 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修饰符用于在ShowDetails类中定义ConvertibleCaroverride修饰符用于在ShowDetails类中定义Minivan

// 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无法访问ShowDetails类中定义的ConvertibleCar版本,因为该方法是用new修饰符声明,而不是override修饰符。 因此,对象 ConvertibleCar 显示与对象相同的说明 Car 。 比较 car3 的结果,这是一个 Minivan 对象。 在这种情况下,ShowDetails 类中声明的 Minivan 方法会替代 ShowDetails 类中声明的 Car 方法,显示的说明描述小型货车。

// 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为 的对象的列表。 对象的值从CarConvertibleCarMinivan类实例化。 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所显示的输出相同。 不调用 ShowDetails 类的 ConvertibleCar 方法,不管该对象的类型是 ConvertibleCar(在 TestCars1 中)还是 Car(在 TestCars2 中)。 相反,无论它是car3类型还是ShowDetails类型,这两种情况下Minivan都会从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,首先从声明为具有类型ConvertibleCarMinivan的对象(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.");  
        }  
    }  
  
}  

另请参阅