TLTR: because of Open-Close principle.
Why it makes sense to have abstract members protected
instead of public
is, from what I can see, to hide implementation details. It is convenient to expose one single "entry point" if you want to ensure that the intent of which each abstract member is defined inside the class is preserved. Normally, methods like "Execute", "Parse", "Start", are such methods, as @Sam Aleksov already pointed out. It is these public method which will orchestrate when and what abstract members are called or accessed and in what particular order and under what circumstances, but the tradeoff for this layer of encapsulation is that it reduces extensibility.
Explanation:
Suppose we create a library with multiple exception handler classes.
Initial implementation:
namespace MyLibrary;
public abstract class ExceptionHandlerBase
{
protected abstract void HandleException(Exception ex, Action operation);
public void Execute(Action operation)
{
try {
operation.Invoke();
} catch(Exception ex) {
this.HandleException(ex, operation);
}
}
}
public class InputExceptionHandler: ExceptionHandlerBase
{
protected override void HandleException(Exception ex, Action operation)
{
throw new Exception(
message: "Wrong input", // or whatever...
innerException: ex);
}
}
public class DbExceptionHandler : ExceptionHandlerBase
{
protected override void HandleException(Exception ex, Action operation)
{
Console.WriteLine("Failed to connect to database. Retrying...");
operation. Invoke();
}
}
Now, if we want to extend the behavior of ExceptionHandlerBase
we will see that we are limited because of that protected
access modifier of ExceptionHandlerBase.HandleException
method.
Let's try to add a hook before ExceptionHandlerBase.HandleException
method:
class ExceptionHandlerWrapper : ExceptionHandlerBase
{
readonly ExceptionHandlerBase _impl;
public ExceptionHandlerWrapper(ExceptionHandlerBase impl)
{
thos._impl= impl;
}
protected override void HandleException(Exception ex, Action operation)
{
this.BeforeHandleException();
this._impl.HandleException(ex, operation); // Compile error**
}
private void BeforeHandleException()
{
// do additional stuff
}
}
As you can see, there is a compilation error because ExceptionHandlerBase.HandleException
is not accessible from outside the class that defines it.