C# 是否可以覆盖非虚拟方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1853896/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 21:24:36  来源:igfitidea点击:

Is it possible to override a non-virtual method?

c#overridingoverloading

提问by zfedoran

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.GraphicsDevicewith unit testing in mind.

我想从Microsoft.Xna.Framework.Graphics.GraphicsDevice单元测试中覆盖一个方法。

采纳答案by Andrew Hare

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

不,您不能覆盖非虚拟方法。您可以做的最接近的事情是通过创建一个new具有相同名称的方法来隐藏该方法,但这不是可取的,因为它违反了良好的设计原则。

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 Mmethod 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 doesbreak polymorphism.

在这个例子中,都调用了M方法 print Foo.M。正如您所看到的,只要对该对象的引用是正确的派生类型,这种方法确实允许您为该方法提供新的实现,但隐藏基方法确实会破坏多态性。

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.

我倾向于支持 C# 的默认行为,即默认方法是非虚拟的(与 Java 相对)。我会更进一步说,默认情况下也应该密封类。继承很难正确设计,并且存在未标记为虚拟的方法这一事实表明该方法的作者从未打算覆盖该方法。

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.Fooin that case, the CLR would correctly determine the type of object that the breference points to as Barand would dispatch the call to Mappropriately.

如果我b.Foo在这种情况下调用,CLR 将正确确定b引用指向的对象类型,Bar并将调用M适当地分派到。

回答by ChrisF

No you can't.

不,你不能。

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

您只能覆盖虚拟方法 - 请参阅此处MSDN

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

  • The base class method must be defined virtual.

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

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

回答by slugster

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.

如果基类不是密封的,那么您可以从它继承并编写一个隐藏基类的新方法(在方法声明中使用“new”关键字)。否则,您无法覆盖它,因为原始作者从未打算将其覆盖,因此它不是虚拟的。

回答by Rory

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).

如果方法是虚拟的,您可以使用派生类中的 override 关键字覆盖它。但是,非虚拟方法只能通过使用 new 关键字代替 override 关键字来隐藏基本实现。如果调用者通过类型为基类型的变量访问方法,则非虚拟路由是无用的,因为编译器将使用静态分派到基方法(意味着永远不会调用派生类中的代码)。

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.

永远不会阻止您向现有类添加重载,但只有了解您的类的代码才能访问它。

回答by ShivanandSK

There is a way of achieving this using abstract class and abstract method.

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

Consider

考虑

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

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Now, if you wish to have different versions of method MethodToBeTested(), then change Class Base to an abstract class and method MethodToBeTested() as an abstract method

现在,如果您希望有不同版本的 MethodToBeTested() 方法,则将 Class Base 更改为抽象类,并将 MethodToBeTested() 方法更改为抽象方法

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

With abstract void MethodToBeTested() comes an issue; the implementation is gone.

抽象 void MethodToBeTested() 带来了一个问题;实施消失了。

Hence create a class DefaultBaseImplementation : Baseto have the default implementation.

因此创建一个class DefaultBaseImplementation : Base具有默认实现。

And create another class UnitTestImplementation : Baseto have unit test implementation.

并创建另一个class UnitTestImplementation : Base以进行单元测试实现。

With these 2 new classes, the base class functionality can be overridden.

使用这 2 个新类,可以覆盖基类功能。

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

}

Class UnitTestImplementation : Base
{

    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }

}

Now you have 2 classes implementing (overriding) MethodToBeTested().

现在您有 2 个类实现 (overriding) MethodToBeTested()

You can instantiate the (derived) class as required (i.e. either with base implementation or with unit test implementation).

您可以根据需要(即使用基本实现或单元测试实现)实例化(派生)类。

回答by user3853059

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.

如果您从非派生类继承,您可以简单地创建一个抽象超类并从它下游继承。

回答by Aaron Thomas

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 canuse the newmodifier keyword to get similar results:

您不能覆盖非虚拟方法。但是,您可以使用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());

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

您还需要确保访问修饰符也相同,否则您将无法获得继承。如果从另一个类继承Class1new关键字Class1不会影响对象从它继承,除非访问修饰符是一样的。

If the access modifier is notthe 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 isthe 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.

正如之前的回答中所指出的,这不是一个好的设计原则。

回答by Vyacheslav Napadovsky

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

您不能在 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();
    }
}

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

    }
}