Forcing some constraints to overridden methods to obey #4022
-
We are often faced with a situation in which we need to figure out whether an overridden method must invoke its base and what the order of invocation is. I think forcing the constraints in code is beneficial. I propose new qualifiers
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Sounds like a situation that could be solved with an analyzer. You could adorn the virtual method with an attribute that specifies how you'd like the base call to be enforced and the analyzer would ensure that the method body has the call at the appropriate location. |
Beta Was this translation helpful? Give feedback.
-
There's also a presumption that the original developer of the base class can entirely anticipate all the extension scenarios for every possible descendent. I don't find this plausible even for base classes I'm writing for my own consumption, let alone as an author of a library that others would extend - there are too many ways that a descendent might want to augment the base class that still respect the functionality provided. As a very simple counter example, creating a descendent that purely (and only) logs entry and exit to methods can be quite useful for understanding the order of interactions: // Defined by us as a consumer
public class LoggingWidget :Base
{
private readonly ILogger _logger;
public LoggingWidget(ILogger logger) => _logger = logger;
protected override void A()
{
_logger.LogEntry("A()"); // Illegal because it's before the call to base
base.A(); // must be called FIRST
_logger.LogExit("A()");
}
protected override void B()
{
_logger.LogEntry("B()");
base.A(); // must be called LAST
_logger.LogExit("B()"); // Illegal because it's after the call to base
}
protected override void C()
{
_logger.LogEntry("C()");
base.A(); // must be called anywhere
_logger.LogExit("B()");
}
protected override void D()
{
_logger.LogEntry("D()");
// for back compatibility
_logger.LogExit("D()");
}
} Enforce your own policy with your own Analyzer and you can add in the flexibility to permit cases like this without ending up with the functionality you want bricked up and inaccessible. |
Beta Was this translation helpful? Give feedback.
There's also a presumption that the original developer of the base class can entirely anticipate all the extension scenarios for every possible descendent.
I don't find this plausible even for base classes I'm writing for my own consumption, let alone as an author of a library that others would extend - there are too many ways that a descendent might want to augment the base class that still respect the functionality provided.
As a very simple counter example, creating a descendent that purely (and only) logs entry and exit to methods can be quite useful for understanding the order of interactions: