是否可以重写一个非虚方法?

有没有办法来重写一个非虚拟的方法? 或者类似的结果(除了创build一个新的方法来调用所需的方法)?

我想重写一个来自Microsoft.Xna.Framework.Graphics.GraphicsDevice的方法,考虑到unit testing。

不,您不能覆盖非虚拟方法。 你可以做的最接近的事情是通过创build一个同名的new方法来隐藏这个方法,但这不是可取的,因为它打破了良好的devise原则。

但是即使隐藏一个方法也不会给你执行时间的方法调用的多态调度,就像真正的虚拟方法调用一样。 考虑这个例子:

 using System; class Example { static void Main() { Foo f = new Foo(); fM(); Foo b = new Bar(); bM(); } } class Foo { public void M() { Console.WriteLine("Foo.M"); } } class Bar : Foo { public new void M() { Console.WriteLine("Bar.M"); } } 

在这个例子中,调用M方法print Foo.M 正如你所看到的,只要对该对象的引用具有正确的派生types,但是隐藏一个基本方法却会破坏多态性,这种方法确实可以让你有一个方法的新实现。

我build议你不要以这种方式隐藏基本方法。

我倾向于支持那些赞成C#默认行为的方法,默认情况下方法是非虚拟的(与Java相对)。 我会更进一步说,class级也应该被默认密封。 inheritance是很难正确devise的,并且有一个方法没有被标记为虚拟的事实表明该方法的作者从来没有打算将该方法重写。

编辑:“执行时间多态调度”

我的意思是在调用虚拟方法时执行时发生的默认行为。 比方说,在我之前的代码示例中,我没有定义一个非虚方法,而是实际上定义了一个虚方法和一个真正的重写方法。

如果我在这种情况下调用b.Foo ,CLR会正确地确定b参考指向Bar的对象的types,并将调用适当的调度给M

不,你不能。

您只能覆盖虚拟方法 – 请参阅此处的MSDN :

在C#中,派生类可以包含与基类方法同名的方法。

  • 基类方法必须定义为虚拟的。

如果基类不是密封的,那么你可以inheritance它,并写一个隐藏基类的新方法(在方法声明中使用“new”关键字)。 否则,你不能覆盖它,因为它从来没有原来的作者意图它被覆盖,因此为什么它不是虚拟的。

我认为你得到重载和覆盖困惑,重载意味着你有两个或更多的方法具有相同的名称,但不同的参数集,而覆盖意味着你有一个派生类中的方法不同的实现(从而取代或修改的行为在它的基类)。

如果一个方法是虚拟的,你可以使用override类关键字来覆盖它。 但是,非虚方法只能使用new关键字代替override关键字来隐藏基本实现。 如果调用者通过types为基types的variables来访问方法,那么非虚拟路由是无用的,因为编译器会使用基本方法的静态调度(意味着永远不会调用你的指派类中的代码)。

从来没有任何东西阻止您为现有的类添加重载,但只有知道您的类的代码才能访问它。

如果您从非派生类inheritance,则可以简单地创build一个抽象超类,并从其inheritance。

有没有办法来重写一个非虚拟的方法? 或者类似的结果(除了创build一个新的方法来调用所需的方法)?

您不能覆盖非虚拟方法。 但是,您可以使用new修饰符关键字来获得类似的结果:

 class Class0 { public int Test() { return 0; } } class Class1 : Class0 { public new int Test() { return 1; } } . . . // result of 1 Console.WriteLine(new Class1().Test()); 

你也要确保访问修饰符也是一样的,否则你不会得到inheritance。 如果另一个类inheritance自Class1那么Class1new关键字不会影响从它inheritance的对象,除非访问修饰符相同。

如果访问修饰符一样:

 class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // different access modifier new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 0 Console.WriteLine(new Class2().Result()); 

…与访问修饰符是否相同:

 class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // same access modifier protected new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 1 Console.WriteLine(new Class2().Result()); 

正如前面的答复所指出的,这不是一个好的devise原则。

有一种使用抽象类和抽象方法实现这一点的方法。

考虑

 Class Base { void MethodToBeTested() { ... } void Method1() { } void Method2() { } ... } 

现在,如果您希望MethodToBeTested()方法的版本不同,那么将Class Base更改为抽象类和方法MethodToBeTested()作为抽象方法

 abstract Class Base { abstract void MethodToBeTested(); void Method1() { } void Method2() { } ... } 

抽象无效MethodToBeTested()出现问题; 实施没有了。

因此,创build一个class DefaultBaseImplementation : Base来拥有默认的实现。

并创build另一个class UnitTestImplementation : Base来进行unit testing的实现。

有了这两个新类,基类function就可以被覆盖。

 Class DefaultBaseImplementation : Base { override void MethodToBeTested() { //Base (default) implementation goes here } } Class UnitTestImplementation : Base { override void MethodToBeTested() { //Unit test implementation goes here } } 

现在你有2个类实现(覆盖) MethodToBeTested()

您可以根据需要实例化(派生)类(即使用基本实现或使用unit testing实现)。