English 中文(简体)
Is it possible to override a non-virtual method?
原标题:

Is there any way to override a non-virtual method? or something that gives similar results (other than creating a new method to call the desired method)?

I would like to override a method from Microsoft.Xna.Framework.Graphics.GraphicsDevice with unit testing in mind.

最佳回答

No, you cannot override a non-virtual method. The closest thing you can do is hide the method by creating a new method with the same name but this is not advisable as it breaks good design principles.

But even hiding a method won t give you execution time polymorphic dispatch of method calls like a true virtual method call would. Consider this example:

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");
    }
}

In this example both calls to the M method print Foo.M. As you can see this approach does allow you to have a new implementation for a method as long as the reference to that object is of the correct derived type but hiding a base method does break polymorphism.

I would recommend that you do not hide base methods in this manner.

I tend to side with those who favor C# s default behavior that methods are non-virtual by default (as opposed to Java). I would go even further and say that classes should also be sealed by default. Inheritance is hard to design for properly and the fact that there is a method that is not marked to be virtual indicates that the author of that method never intended for the method to be overridden.

Edit: "execution time polymorphic dispatch":

What I mean by this is the default behavior that happens at execution time when you call virtual methods. Let s say for example that in my previous code example, rather than defining a non-virtual method, I did in fact define a virtual method and a true overridden method as well.

If I were to call b.Foo in that case, the CLR would correctly determine the type of object that the b reference points to as Bar and would dispatch the call to M appropriately.

问题回答

No you can t.

You can only override a virtual method - see the MSDN here:

In C#, derived classes can contain methods with the same name as base class methods.

  • The base class method must be defined virtual.

You can t override non-virtual method of any class in C# (without hacking CLR), but you can override any method of interface the class implements. Consider we have non-sealed

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();
    }
}

And here s demo

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();

    }
}

If the base class isn t sealed then you can inherit from it and write a new method that hides the base one (use the "new" keyword in the method declaration). Otherwise no, you cannot override it because it was never the original authors intent for it to be overridden, hence why it isn t virtual.

I think you re getting overloading and overriding confused, overloading means you have two or more methods with the same name but different sets of parameters while overriding means you have a different implementation for a method in a derived class (thereby replacing or modifying the behaviour in it s base class).

If a method is virtual, you can override it using the override keyword in the derrived class. However, non-virtual methods can only hide the base implementation by using the new keyword in place of the override keyword. The non-virtual route is useless if the caller accesses the method via a variable typed as the base type as the compiler would use a static dispatch to the base method (meaning the code in your derrived class would never be called).

There is never anything preventing you from adding an overload to an existing class, but only code that knows about your class would be able to access it.

The answer to this question is not entirely No. It depends on how you structure your inheritance and access the instances of your classes. If your design meets the following, you would be able to override non-virtual method from base class with the new modifier:

  • The method is declared in an interface that your classes inherit from
  • You are accessing the class instances using the interface

Take example of the following:

interface ITest { void Test(); }
class A : ITest { public void Test(){Console.WriteLine("A");} }
class B : A { public new void Test(){Console.WriteLine("B");} }
ITest x = new B(); 
x.Test(); // output "A"

calling x.Test() will output "A" to the console. However if you re-declare the interface in the definition of class B, x.Test() will output B instead.

interface ITest { void Test(); }
class A : ITest { public void Test(){Console.WriteLine("A");} }
class B : A, ITest { public new void Test(){Console.WriteLine("B");} }
ITest x = new B();
x.Test(); // output "B"

In the case you are inheriting from a non-derived class, you could simply create an abstract super class and inherit from it downstream instead.

Is there any way to override a non-virtual method? or something that gives similar results (other than creating a new method to call the desired method)?

You cannot override a non-virtual method. However you can use the new modifier keyword to get similar results:

class Class0
{
    public int Test()
    {
        return 0;
    }
}

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

You will also want to make sure that the access modifier is also the same, otherwise you will not get inheritance down the line. If another class inherits from Class1 the new keyword in Class1 will not affect objects inheriting from it, unless the access modifier is the same.

If the access modifier is not the same:

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());

...versus if the access modifier is the same:

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());

As pointed out in a previous answer, this is not a good design principle.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签