有什么方法可以覆盖非虚拟方法?或产生类似结果的东西(除了创建一个新方法来调用所需的方法之外)?
我想Microsoft.Xna.Framework.Graphics.GraphicsDevice
在考虑单元测试的情况下覆盖其中的一种方法。
Answers:
不可以,您不能覆盖非虚拟方法。您可以做的最接近的事情是通过创建一个new
具有相同名称的方法来隐藏该方法,但这是不可取的,因为它违反了良好的设计原则。
但是,即使隐藏方法也不会像真正的虚拟方法调用那样为您提供执行时间。考虑以下示例:
using System;
class Example
{
static void Main()
{
Foo f = new Foo();
f.M();
Foo b = new Bar();
b.M();
}
}
class Foo
{
public void M()
{
Console.WriteLine("Foo.M");
}
}
class Bar : Foo
{
public new void M()
{
Console.WriteLine("Bar.M");
}
}
在此示例中,两个M
方法都调用print Foo.M
。如您所见,只要对该对象的引用具有正确的派生类型,但此方法的确可以为该方法提供新的实现,但是隐藏基本方法确实会破坏多态性。
我建议您不要以这种方式隐藏基本方法。
我倾向于支持C#默认行为的那些人,即默认情况下方法是非虚拟的(与Java相对)。我还要说的是,默认情况下也应该密封类。很难为继承进行适当的设计,并且存在一个没有被标记为虚拟的方法的事实表明该方法的作者从未打算重写该方法。
编辑:“执行时间多态调度”:
我的意思是这是调用虚拟方法时在执行时发生的默认行为。举例来说,在我之前的代码示例中,实际上没有定义虚拟方法,而是定义了虚拟方法,而不是定义非虚拟方法。
如果要b.Foo
在这种情况下进行调用,则CLR将正确确定b
引用所指向的对象的类型,Bar
并M
适当地将调用分派给该对象。
您不能覆盖C#中任何类的非虚拟方法(不对CLR进行黑客攻击),但是可以覆盖该类实现的接口的任何方法。考虑我们没有密封
class GraphicsDevice: IGraphicsDevice {
public void DoWork() {
Console.WriteLine("GraphicsDevice.DoWork()");
}
}
// with its interface
interface IGraphicsDevice {
void DoWork();
}
// You can't just override DoWork in a child class,
// but if you replace usage of GraphicsDevice to IGraphicsDevice,
// then you can override this method (and, actually, the whole interface).
class MyDevice: GraphicsDevice, IGraphicsDevice {
public new void DoWork() {
Console.WriteLine("MyDevice.DoWork()");
base.DoWork();
}
}
这是演示
class Program {
static void Main(string[] args) {
IGraphicsDevice real = new GraphicsDevice();
var myObj = new MyDevice();
// demo that interface override works
GraphicsDevice myCastedToBase = myObj;
IGraphicsDevice my = myCastedToBase;
// obvious
Console.WriteLine("Using real GraphicsDevice:");
real.DoWork();
// override
Console.WriteLine("Using overriden GraphicsDevice:");
my.DoWork();
}
}
有什么方法可以覆盖非虚拟方法?或产生类似结果的东西(除了创建一个新方法来调用所需的方法之外)?
您不能覆盖非虚拟方法。但是,您可以使用new
modifier关键字获得类似的结果:
class Class0
{
public int Test()
{
return 0;
}
}
class Class1 : Class0
{
public new int Test()
{
return 1;
}
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());
您还需要确保access修饰符也相同,否则您将无法获得继承。如果从另一个类继承Class1
的new
关键字Class1
不会影响对象从它继承,除非访问修饰符是一样的。
如果访问修饰符不相同:
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());
如先前的回答所指出,这不是一个好的设计原则。
有一种使用抽象类和抽象方法来实现此目的的方法。
考虑
Class Base
{
void MethodToBeTested()
{
...
}
void Method1()
{
}
void Method2()
{
}
...
}
现在,如果希望使用不同版本的MethodMethodToBeTested()方法,则将Class Base更改为抽象类,并将MethodToBeTested()方法更改为抽象方法
abstract Class Base
{
abstract void MethodToBeTested();
void Method1()
{
}
void Method2()
{
}
...
}
随着抽象void MethodToBeTested()的出现;实施不见了。
因此,创建一个class DefaultBaseImplementation : Base
具有默认实现的。
并创建另一个class UnitTestImplementation : Base
具有单元测试的实现。
使用这两个新类,可以覆盖基类功能。
Class DefaultBaseImplementation : Base
{
override void MethodToBeTested()
{
//Base (default) implementation goes here
}
}
Class UnitTestImplementation : Base
{
override void MethodToBeTested()
{
//Unit test implementation goes here
}
}
现在,您有两个实现(覆盖)的类MethodToBeTested()
。
您可以根据需要实例化(派生的)类(即使用基本实现或单元测试实现)。