我可以使用 Task.CompletedTask 来检测虚方法是否被覆盖了吗?

Can I Use Task.CompletedTask to Detect if Virtual Method was Overridden?

我有一个 asp.net 核心 Web 应用程序,它有一个抽象 class 和一个 child classes 可能会或可能不会覆盖的虚拟方法。我想根据它是否被覆盖来执行不同的逻辑,并且想知道下面的检查是否是个好主意:

public abstract class BaseClass
{
    public async Task DoSomethingAsync()
    {
        var task = OverrideMe();
        bool wasOverridden = task != Task.CompletedTask;
        await task;

        if (wasOverridden)
        {
            //Virtual method was overridden
        }
        else
        {
            //Virtual method was not overridden
        }
    }

    protected virtual Task OverrideMe()
    {
        return Task.CompletedTask;
    }
}

我意识到如果 child class returns Task.CompletedTask 它看起来仍然没有被覆盖。但是如果我们忽略这个事实,这个检查会起作用吗?我最关心的是任务的Id字段。这会改变并可能导致相等比较失败吗?当我测试上面的代码时,它可以工作,但是如果有其他并行任务 运行 等怎么办?

如果在方法没有覆盖的情况下设置私有字段会怎样:

public abstract class BaseClass
{
    private bool _notOverwritten;
    public async Task DoSomethingAsync()
    {
        var task = OverrideMe();
        
        await task;

        if (!_notOverwritten)
        {
            //Virtual method was overridden
        }
        else
        {
            //Virtual method was not overridden
        }
    }

    protected virtual Task OverrideMe()
    {
        _notOVerwritten = true;

        return Task.CompletedTask;
    }
}

编辑:这个解决方案并不完全有效,因为派生的 class 仍然可以像 @Theodor Zoulias 提到的那样调用 base.OverrideMe()

您可以在调用 base.OverrideMe() 时抛出异常并在 DoSomethingAsync 中捕获它,但是派生的 class 也可以捕获此异常。

首先,我推荐re-addressing设计。如果可能,当派生 class 覆盖方法时,基 class 不应更改行为。也许基类型应该定义一个 属性 的接口类型,派生类型可以选择设置或保留 null。最重要的是,从设计的角度来看,“检测是否存在覆盖”是一个巨大的危险信号。

也就是说,在我(将近)25 年的编程生涯中,我曾经做过一次。我的 ConnectedProperties 库有一个验证检查以尝试确保正确使用;连接属性仅适用于被视为引用类型的类型,因此它检查 if the type overrides Equals 作为“类型被视为值类型”的启发式方法。

如果您必须使用这个有问题的设计,那么反射是唯一可行的方法;在该类型层次结构中找到与 OverrideMe 的名称和参数相匹配的方法,然后检查它们的基本定义是否是来自基本类型的 OverrideMe