Back to Blog
C#

C# Protected Inheritance: Access in Derived Classes

c# protected inheritance: Understand how the protected access modifier works in C# inheritance, including access rules, derived class usage, and design tradeoffs.

C# inheritanceprotected access modifieraccess modifiersderived classesobject-oriented design
Illustration of a C# class hierarchy with protected members visible only to derived classes, shown as a shield around the base class.

c# protected inheritance requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

In C#, the term "protected inheritance" often confuses developers coming from C++. C# does not have a separate inheritance mode called protected. Instead, the protected access modifier controls visibility of members within an inheritance hierarchy. This article explains how protected members behave in C#, how they interact with derived classes, and where they are best applied in real code.

What Protected Inheritance Means in C#

When you mark a member as protected, you are making it accessible from two places: the class that declares it and any class that derives from that class. This is different from private, which restricts access to the declaring class only, and public, which allows access from anywhere.

public class Base { protected int Counter; } public class Derived : Base { public void Increment() { Counter++; // Allowed because Derived inherits from Base } }

In this example, Counter is not accessible from outside the inheritance hierarchy. A public method on Derived can access it, but a completely unrelated class cannot. This is the core of what developers usually mean by "protected inheritance" in C#: the protected members become part of the derived class's internal contract.

Access Rules for Protected Members

The C# specification defines precise rules for protected access. A protected member can be accessed within the body of a derived class, but only through an instance of that derived class or a further derived type. You cannot access a protected member through a base class reference, even from within a derived class.

public class Derived2 : Base { public void Check(Base other) { // other.Counter = 5; // Compiler error: cannot access protected member via Base reference } public void CheckSelf() { Counter = 5; // OK, this is a Derived2 instance } }

This rule prevents a derived class from manipulating protected members of other derived classes through a base reference. It ensures that each derived class only controls its own inherited state, not the state of sibling classes.

Protected Members and Derived Class Instances

A protected member is not visible to code outside the inheritance chain. For example, if you have an instance of Derived, you cannot call a protected method or read a protected property from a consumer of that class.

public class Consumer { public void Use() { var d = new Derived(); // d.Counter = 10; // Compiler error: 'Counter' is inaccessible due to its protection level } }

This behavior is intentional. Protected members are part of the implementation contract between a base class and its derived classes, not part of the public API. They allow derived classes to share common implementation details without exposing them to the outside world.

Protected Internal and Private Protected Combinations

C# provides two additional access modifiers that combine protected with other scopes:

  • protected internal: accessible from the same assembly or from any derived class, regardless of assembly.
  • private protected: accessible from derived classes that are also in the same assembly. This is available since C# 7.2.
public class Base { protected internal int InternalProtectedValue; private protected int PrivateProtectedValue; } public class DerivedSameAssembly : Base { public void Access() { InternalProtectedValue = 1; // OK PrivateProtectedValue = 2; // OK because same assembly } }

If DerivedOtherAssembly is in a different assembly, it can access InternalProtectedValue but not PrivateProtectedValue. These combinations give you fine-grained control when you are building libraries and need to limit cross-assembly access.

Overriding and Protected Virtual Members

Protected members are often used with virtual and override to provide extension points for derived classes. A protected virtual method can define a default behavior that derived classes can replace or extend.

public class Base { protected virtual void Initialize() { // Default setup } public void Start() { Initialize(); } } public class Derived : Base { protected override void Initialize() { base.Initialize(); // Additional setup } }

Here, Initialize is protected so that only derived classes can override it. The public Start method calls it, but external callers cannot invoke Initialize directly. This pattern is common in template method designs where the base class controls the algorithm and derived classes customize specific steps.

Design Considerations: When to Use Protected

Choosing protected over private or public depends on the intended relationship between the base class and its derived classes. Use protected when you want to share implementation details with subclasses but keep them hidden from consumers. This often applies to helper methods, state that subclasses need to maintain, or hooks for customization.

However, protected members increase the coupling between a base class and its derived classes. Every protected member becomes part of the inheritance contract, and changing it can break derived classes. Before adding a protected member, consider whether the derived class truly needs direct access or whether a protected method that encapsulates the operation would be safer.

A common mistake is exposing fields as protected. Fields are mutable state, and allowing derived classes to change them directly can lead to inconsistent behavior. Prefer protected properties or methods that enforce invariants.

Common Mistakes with Protected Members

One frequent error is attempting to access a protected member from a derived class using a base class reference, as shown earlier. Another is assuming that protected members are visible to all classes in the same assembly. They are not; they are only visible to derived classes.

Another mistake is overusing protected members to expose internal details that should remain private. This weakens encapsulation and makes the base class harder to evolve. For example, a protected field that is only used by one derived class could be moved to that derived class as a private field.

Finally, be careful when combining protected with virtual. If a derived class overrides a protected virtual method, the base class may rely on that method being called at specific times. If the override forgets to call base.Method(), the base behavior is lost. Document these expectations clearly or design the base method to be safe to skip.

Maintainability and Inheritance Design

Protected members are a double-edged sword. They enable flexible inheritance hierarchies but also create hidden dependencies. When you change a protected member, you must consider every derived class that might use it. This is especially important in large codebases or when shipping a library to external consumers.

To keep your code maintainable, prefer protected methods over protected fields, limit the number of protected members to what is truly needed, and document the intended usage of each one. If a derived class needs access to a value but should not modify it arbitrarily, expose a protected read-only property. If a derived class needs to customize behavior, provide a protected virtual method with a clear contract.

By following these guidelines, you can use the protected access modifier effectively without turning your base classes into fragile hubs of hidden state.

c# protected inheritance: Practical Usage and Code Examples | RYUSLOG DEV