共计 1672 个字符,预计需要花费 5 分钟才能阅读完成。
今天一朋友问我,C# 中 new 与 override 有什么区别?
附上原问题导航 C# 中的 new 与 Override 的区别?(SF 上的各位都是朋友,嘿嘿)。
刚看到这个问题,其实我有点懵,因为我对这两个关键字的研究也没有花太多心思,虽然知道一些区别,但并不是非常确信。
所以,趁着这次机会,自己也学习一下。
首先,原问题中的示例代码给了我非常好的参考。这段代码非常直观地体现了 new
和override
的区别。
下面是我略加整理的测试代码:
new
修饰符
代码
public class FatherNew
{protected void DoConsole()
{Console.WriteLine("FatherNew");
}
public void ConsoleFather()
{DoConsole();
}
}
public class ChildNew : FatherNew
{protected new void DoConsole()
{Console.WriteLine("ChildNew");
}
}
// 客户端调用
var cn = new ChildNew();
cn.ConsoleFather();
代码中,我们使用 new
隐藏了父类(FatherNew
)的 DoConsole
方法。
看看它的运行结果吧。
运行结果
那么问题来了:我们明明把 Father
的DoConsole
方法覆盖掉了,理论上 ConsoleFather()
执行的时候应该调用新的 DoConsole
呀,这样的话,应该输出 "ChildNew"
才对呀。
这个疑惑咱们先放放,先看一下 override
的代码及结果咱们再统一总结。
override
修饰符
代码
public class FatherOverride
{public virtual void DoConsole()
{Console.WriteLine("FatherOverride");
}
public void ConsoleFather()
{DoConsole();
}
}
public class ChildOverride : FatherOverride
{public override void DoConsole()
{Console.WriteLine("ChildOverride");
}
}
// 客户端调用
var co = new ChildOverride();
co.ConsoleFather();
就像 new
里的代码一样,我们重写了父类(FatherOverride
)的 DoConsole
方法,只不过这次是用 override
修饰符。
也看看运行结果吧:
运行结果
这个还算符合预期,既然我重写了 DoConsole
,那么执行时,就应该执行调用新的DoConsole
才对嘛。
结论
new
修饰符定义的新方法,在同一派生类调用时,的确会调用这个新的方法,但在其基类中调用时,依旧是调用基类中的那个原始的方法。override
修饰符重写的方法,只要是通过派生类实例调用,哪怕是间接调用(比如像示例代码中,派生类的示例,调用父类中的 ConsoleFather
方法),都会调用新的使用 override
重写的那个方法。
之所以出现这种情况,是因为在编译时,它们就已经有了差异:
普通成员在编译时,其引用的相对地址就已经被程序确定好了。因此程序运行时,将会直接根据该地址调用该成员。即:在编译时,
FatherNew
的ConsoleFather
方法中,就已经把DoConsole
方法的引用确定好了(也就是FatherNew
的DoConsole
方法的引用)。而虚成员在编译时,并没有将其引用固定写在程序的执行文件中。因此程序运行时,才会检查当前对象(也就是类的实例)到底是什么类型。当该对象调用虚成员时,会发现该成员是虚成员,然后就会查找它的重写成员。从该实例的类开始,一层一层地向上(即其父类)查找,找到后就会执行。即:
ChildOverride
实例化后,调用其基类中的ConsoleFather
时,程序还不知道DoConsole
的具体引用地址。查找后发现这个虚方法的第一个重写就是ChildOverride
中的DoConsole
,因此,就执行它了。