在面向对象编程(OOP)中,隐藏(Shadowing)是一个重要的概念,它允许在派生类中为基类成员提供新的实现,而不需要覆盖它。这意味着基类成员的原始实现被派生类中提供的新的实现所隐藏。
设想一个场景,在一个项目中添加了一个外部程序集,并且该程序集中有一个类,其中有一个方法没有被定义为虚拟的,但希望在派生类中覆盖这个方法(为这个方法定义自己的实现)。在这种情况下,可以使用隐藏的概念来在派生类中覆盖这个方法。
以下是隐藏的几个定义:
根据MSDN的定义,隐藏是面向对象编程中多态性的一种概念。当两个编程元素共享相同的名称时,其中一个可以隐藏或覆盖另一个。在这种情况下,被隐藏的元素不可用,而是当代码使用元素名称时,编译器将其解析为隐藏的元素。
隐藏实际上是在派生类中隐藏覆盖的方法实现,并使用派生类对象调用父类实现。
隐藏和覆盖之间有一个主要的区别。通常,当在派生类中覆盖一个虚拟方法,并创建派生类的实例,然后如果持有派生类对象的基类引用,并调用该成员,它总是调用派生类实现,这是应该发生的。但在隐藏的情况下,情况就不同了,如果在派生类中使用new关键字隐藏同一个虚拟成员,并且像上面那样调用实现,它将调用基类实现,当有基类类型的对象引用时,如果有相同对象的派生类型引用,它将调用派生类型实现,因此基类和派生类的实现彼此隐藏,调用哪个实现的方法取决于否使用基类或派生类的引用来调用成员。
假设有一个基类BaseLogger,它定义了两个虚拟方法(意味着它们可以在子类中被覆盖):
public abstract class BaseLogger {
public virtual void Log(string message) {
Console.WriteLine("Base: " + message);
}
public virtual void LogCompleted() {
Console.WriteLine("Completed");
}
}
现在,创建了一个类Logger,它继承自BaseLogger类。Logger类如下所示:
public class Logger : BaseLogger {
public override void Log(string message) {
Console.WriteLine(message);
}
public new void LogCompleted() {
Console.WriteLine("Finished");
}
}
现在,希望控制台打印以下行:
Log started!
Base: Log Continuing
Completed
应该在主程序中写什么代码来获得上述输出?这是需要编写的代码:
public class Program {
public static void Main() {
BaseLogger logger = new Logger();
logger.Log("Log started!");
logger.Log("Base: Log Continuing");
logger.LogCompleted();
}
}
第一个对Log方法的调用是正确的,它调用了派生Logger类Log方法,应该这样做,因为在Logger类中覆盖了它。第二个调用也是一样的。
现在注意第三个调用,它调用了基类LogCompleted()方法,而不是派生类,这是因为使用new关键字定义了派生类方法,当使用BaseLogger类型的对象调用它时,它隐藏了派生类方法,如果没有使用new关键字,编译器会认为需要隐藏或覆盖,并且在编译时会显示警告,说:
use the new keyword if hiding was intended