使成员成为虚拟成员可防止调用默认接口实现,并导致C#8中的StackOverflowException [英] Making member virtual prevents calling default interface implementation and causes StackOverflowException in C# 8

查看:72
本文介绍了使成员成为虚拟成员可防止调用默认接口实现,并导致C#8中的StackOverflowException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑代码:

class ChildClass : BaseClass {

    public void Method1() {} //some other method

}

abstract class BaseClass : IChildInterface {

    public
    virtual //<- If we add virtual so that this method can be overridden by ChildClass, we get StackOverflowException and DoWork() implementation in IChildInterface is never called.
    void DoWork() {
     //base class specific implmentation
     ((IChildInterface)this).DoWork(); //call into default implementation provided by IChildInterface
    }

}

interface IChildInterface : IBaseInterface {

    void IBaseInterface.DoWork() {
     //implmentation
    }

}

interface IBaseInterface {

    void DoWork();

}

问题是,如果我们将 BaseClass 中的 DoWork()标记为 virtual ,以便可以被子类覆盖,则会阻止调用 IChildInterface 的默认实现 DoWork(),导致 StackOverflowException .

The problem is that if we mark DoWork() in BaseClass as virtual so that it can be overridden by child classes, it prevents it from calling into IChildInterface's default implementation of DoWork(), causing StackOverflowException.

如果我们从 BaseClass 中的 DoWork()中删除 virtual 修饰符,则一切正常,并且 IChildInterface '的 DoWork() 的默认实现称为.

If we remove virtual modifier from DoWork() in the BaseClass, everything works and the IChildInterface's default implementation of DoWork() is called.

这样的行为是错误还是故意的?

Is such a behavior a bug, or by design?

有没有办法使 some 子类提供自己的 DoWork()实现(从而覆盖了 BaseClass 实现),但仍然可以使用 IChildInterface 的默认实现 DoWork()?

Is there a way to make it possible for some child classes provide their own implementation of DoWork() (thus overriding BaseClass's implementation) but still being able to use IChildInterface's default implementation of DoWork()?

推荐答案

您正在递归地调用 BaseClass.DoWork ,如果幸运的话,这将导致StackOverflowException.如果调用是方法中的最后一个调用,则由于尾部调用的优化,您将获得无限递归.直到您杀死该应用程序为止,您的核心始终停留在100%.

You're calling BaseClass.DoWork recursively which, if you're lucky, will result in a StackOverflowException. If the call was the last one in the method, you'd get an infinite recursion due to tail call optimizations. You'd end up with a core stuck at 100% until you killed the app.

此代码:

public virtual void DoWork() {
   ((IChildInterface)this).DoWork(); by IChildInterface
}

与:

//That's the actual implementation of the interface method
public virtual void DoWork() {
     DoWork(); 
}

virtual 关键字无关紧要.没有它,您仍然可以获得无限递归.无论是否存在,此行过一会儿都会引发StackOverflowException:

The virtual keyword doesn't matter. You'd still get infinite recursion without it. Whether it exists or not, this line throws a StackOverflowException after a while :

new ChildClass().DoWork();

当您实现 BaseClass.DoWork 时,它成为每个人都可以使用的单个实现,除非被子类覆盖.

When you implemented BaseClass.DoWork that became the single implementation available to everyone, unless overridden by a child class.

即使在C#8中,接口也不是抽象类.默认的方法实现不是实际的方法.顾名思义,这是默认实现.在没有更好的实现可用时使用.当方法已经在类中实现时,您不能调用默认实现.

Interfaces are not abstract classes, even in C# 8. A default method implementation is not an actual method. As the name says, it's a default implementation. It's used when there's no better implementation available. You can't call the default implementation when the method is already implemented in a class.

实际上,几乎在每种情况下,您都不希望调用默认方法.通过接口显式调用DIM,与使用显式接口实现的方式相同.该方法的调用者希望运​​行最派生的实现,而不是基础或中级实现.

In fact, in almost every case you wouldn't expect the default method to be called. DIMs are called explicitly through the interface, the same way explicit interface implementations are used. Callers of the method expect the most-derived implementation to run, not the base or mid-level one.

此外,即使在以前的C#版本中,您也不会期望转换为接口以更改实际调用的方法.您只希望使用类.要调用基类实现,可以使用 base 关键字.尽管 BaseClass 的基类是 Object ,但没有 DoWork 方法.

Besides, even on previous C# versions you wouldn't expect casting to an interface to change which method is actually called. You'd expect that only with classes. To call a base class implementation you'd use the base keyword. The base class of BaseClass though is Object which doesn't have a DoWork method.

如果您使用过:

void DoWork() {
    base.DoWork(); 
}

您将得到一个 CS0117:对象"不包含"DoWork"的定义

更新

C#设计团队已经对此有所了解.没有运行时支持和已于2019年5月终止.运行时优化使DIM调用与其他调用一样便宜,无需装箱等.

The C# design team has already though about this. This couldn't be implemented efficiently without runtime support and was cut i May 2019. Runtime optimizations is what makes DIM calls as cheap as other calls, without boxing etc.

查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆