Невозможно иметь как абстрактный, так и виртуальный метод.
Если возможно, вы можете разделить ваш метод на части «до» и «после»:
public void DoWork()
{
DoBeforeWork();
DoCommonWork();
DoAfterWork();
}
protected abstract void DoBeforeWork();
protected abstract void DoAfterWork();
private void DoCommonWork() { ... }
В противном случае ваш обходной путь со вторым защищенным методом - очень хорошая идея:
public void DoWork()
{
DoActualWork();
}
protected abstract void DoActualWork(); // expected to call DoCommonWork
protected void DoCommonWork() { ... }
Вы можете проверить, действительно ли DoCommonWork вызывался в DoWork, при необходимости используя локальное поле потока.
Однако я бы, вероятно, пошел с тем, чтобы сделать метод виртуальным. Если производный класс не хочет ничего добавлять к общей части, он не должен:
public virtual void DoWork() { ... }
Опять же, вы можете проверить, действительно ли была названа общая часть.