Back to Blog
C#

C# Protected Constructor Usage and Scenarios

c# protected constructor: Learn when and how to use a protected constructor in C#: syntax, behavior with inheritance, factory patterns, and common mistakes to avoid.

ConstructorsInheritanceAccess ModifiersAbstract ClassesFactory Pattern
Illustration of a C# class diagram showing a protected constructor symbol, representing restricted object creation through inheritance.

A c# protected constructor restricts instantiation to derived classes and to code within the same class. This access level has practical implications for object creation, inheritance hierarchies, and factory-based design. Understanding exactly when a protected constructor is the right choice prevents both overexposure and over-restriction of class construction.

Syntax and Basic Behavior

The syntax follows the standard constructor pattern with the protected modifier:

public class BaseClass { protected BaseClass(string name) { Name = name; } public string Name { get; } } public class DerivedClass : BaseClass { public DerivedClass(string name) : base(name) { } }

In this example, DerivedClass can call the base constructor because it has access through inheritance. The important detail is that external code cannot call new BaseClass("value") directly. That call causes a compile-time error because the constructor is not public.

Protected constructors do not prevent a class from being abstract or sealed. A sealed class can have a protected constructor, but it has little practical value because no derived classes can exist. More often, protected constructors appear on abstract classes or base classes intended for inheritance.

Why Use a Protected Constructor?

A protected constructor enforces a rule about how a class can be instantiated. It says: this class cannot be constructed independently; only derived types or static factory methods within the class hierarchy may create instances.

A common use is an abstract base class that provides shared logic but should not be instantiated itself. Rather than declaring the class abstract, you can rely on a protected constructor to block direct construction while still allowing derived classes to call it.

Consider a base class that centralizes configuration loading:

public abstract class ConfigurationBase { protected ConfigurationBase(string sourcePath) { SourcePath = sourcePath; Load(); } public string SourcePath { get; } protected abstract void Load(); } public class JsonConfiguration : ConfigurationBase { public JsonConfiguration(string path) : base(path) { } protected override void Load() { // JSON-specific loading } }

Here the protected constructor ensures every derived configuration type supplies a source path and triggers the loading sequence. External code cannot create a ConfigurationBase directly, which is the intended behavior.

Interaction with Abstract Classes and Instantiation Rules

If a class is declared abstract, it cannot be instantiated even with a public constructor. Adding protected changes who can call the constructor during a derived type's construction. A protected constructor on an abstract class is common because it makes the construction intent explicit.

A subtle point: a class with no explicit constructor automatically gets a public parameterless constructor if the class is not static and not abstract. If you omit a constructor in a base class, derived classes can implicitly call the default parameterless constructor. That is different from having a protected constructor.

When you define an explicit protected constructor, you also suppress the default public parameterless constructor. This can be a deliberate design decision to force derived classes to provide arguments.

Consider a class that requires a connection string:

public class RepositoryBase { protected RepositoryBase(string connectionString) { ConnectionString = connectionString; } public string ConnectionString { get; } }

Because the only constructor is protected, external code cannot write var repo = new RepositoryBase(...). Derived classes must call base(connectionString). This is a compile-time enforcement of a required dependency.

Protected Constructors and Factories

A protected constructor is often paired with a static factory method inside the same class. Since the class itself can access its own protected members, you can expose a public creation path without making the constructor public.

public class DatabaseConnection { private DatabaseConnection(string host, int port) { Host = host; Port = port; } protected string Host { get; } protected int Port { get; } public static DatabaseConnection Create(string host, int port) { return new DatabaseConnection(host, port); } }

In this case, the constructor is private, not protected. A protected constructor would allow derived classes to call that exact constructor but not create standalone instances. The factory method can return derived types while relying on the protected constructor.

A more typical factory pattern uses an abstract base with a protected constructor:

public abstract class PaymentProcessor { protected PaymentProcessor(string merchantId) { MerchantId = merchantId; } public string MerchantId { get; } public abstract void Process(decimal amount); public static PaymentProcessor Create(string type, string merchantId) { return type == "card" ? new CardProcessor(merchantId) : new BankTransferProcessor(merchantId); } } public class CardProcessor : PaymentProcessor { public CardProcessor(string merchantId) : base(merchantId) { } public override void Process(decimal amount) { // Card-specific processing } } public class BankTransferProcessor : PaymentProcessor { public BankTransferProcessor(string merchantId) : base(merchantId) { } public override void Process(decimal amount) { // Bank transfer processing } }

External code calls PaymentProcessor.Create(...), which returns a concrete derived instance. The protected constructor keeps the base class non-instantiable while allowing the factory to create concrete types. This pattern is useful when you want to centralize decision logic for object creation.

When to Choose Protected Over Private or Public

Choosing the correct access modifier for a constructor depends on who should be allowed to call it.

Constructor AccessAllowed CallersTypical Use Cases
privateonly the class itselfsingleton, immutable value creation, strict factory control
protectedclass and derived typesabstract base class initialization, template method pattern
publicany codenormal direct instantiation

A private constructor forbids even derived classes from calling it. A protected constructor allows derived classes to call it, which is necessary when derived classes rely on base initialization logic. If you want to allow derived classes to construct the base but not allow external direct construction, protected is the correct choice.

One common mistake is making a constructor protected when the class is sealed and has no derived types. That restricts instantiation unnecessarily without any benefit. In such cases, a private constructor is more appropriate.

Another situation is when you need to force all derived classes to go through a specific constructor signature. By providing only a protected constructor that accepts parameters, you prevent derived classes from falling back to an implicit parameterless base constructor. Derived classes must explicitly call base(...), which ensures required dependencies are always passed.

Common Mistakes and Runtime Cautions

One common error is trying to instantiate a class that has a protected constructor from ordinary application code. The compiler will produce an error like CS0122: 'BaseClass.BaseClass(...)' is inaccessible due to its protection level. The fix is to either add a public constructor or use a derived class or factory method.

Another mistake is assuming that a protected constructor prevents reflection-based instantiation. Reflection can still call a protected constructor if the code uses Activator.CreateInstance with non-public constructors or obtains the constructor via GetConstructor(BindingFlags.NonPublic). This is rarely a security boundary; it is a design guideline enforced at compile time. If untrusted code must not create instances, additional checks are necessary.

There is also a runtime consideration: a protected constructor that performs heavy work will be executed every time a derived class is instantiated. Placing resource-grabbing logic in the base constructor means every derived instance incurs that cost. If the base class initializes expensive resources that are shared, consider lazy loading or a separate initialization method instead.

Compatibility and Maintainability Implications

Using a protected constructor affects how easily external code can test or extend your classes. Test projects may need to create derived classes or use reflection to exercise the base class behavior. This adds coupling between tests and inheritance structure. In many cases, a public constructor with protected internal setters is more flexible for testing.

From a maintainability perspective, a protected constructor communicates design intent clearly. The class hierarchy shows that the base class is not meant to be directly constructed. This prevents later developers from accidentally creating instances that lack required setup.

However, overusing protected constructors can make a class hierarchy rigid. If a class is later used as a standalone component rather than a base class, you must change the constructor access to public, which can be a breaking change for existing derived types only if the signature changes. Changing from protected to public does not break existing derived classes, but it expands the access surface.

When evaluating this pattern, consider whether the base class truly represents an incomplete abstraction. If the class has all concrete implementations and is only used for inheritance, an abstract class is usually clearer. The protected constructor works best when you want to allow inheritance but not direct instantiation, while also preserving the ability to have concrete members.

A final maintainability point is to keep constructor logic minimal. A protected constructor is not the right place to perform complex validation across the entire hierarchy. Derived classes may need to know about the base class's initialization order. If a base constructor performs work that depends on virtual method calls, the derived class cannot fully control the execution order. In such cases, a protected parameterless constructor with a separate Initialize method gives derived classes more control.

Setting Up a Clean Inheritance Contract

A protected constructor works well when combined with a protected internal constructor for library scenarios. The protected internal modifier allows access from the same assembly as well as from derived classes in other assemblies. This is useful for libraries that want to expose extensibility to external consumers while keeping the internal API private.

If you need to allow only specific derived types to call the base constructor, C# does not provide a direct syntax for that. The protected modifier gives access to all derived types. To restrict further, you would need to use a private constructor along with a static factory that controls which types can be created.

Consider a scenario where you have a base class that should only be derived by a single sealed child class. In that case, a protected constructor allows that child to call it, but any other class could also derive and call it. If the intent is to allow only one derived type, a protected constructor does not enforce that. You would need a private constructor and a nested factory method that only creates that specific child type.

These tradeoffs matter when designing public APIs. A protected constructor is a stable contract: derived classes must respect it, and external code sees that direct construction is not available. This can be a deliberate design decision to keep object creation centralized.

Code Organization for Protected Constructors

Place the protected constructor near the top of the class, after fields and before properties, following conventional C# style. This makes the construction constraints visible to anyone reading the class.

When a base class has multiple protected constructors, each one should have a clear naming from its parameter list. Use meaningful parameter names and consider whether a particular constructor is intended for specific derived types. Overloading a protected constructor with similar signatures can create ambiguity for derived classes.

If a derived class does not need to pass anything to the base, it can use syntax:

public class DerivedClass : BaseClass { public DerivedClass() : base() { } }

But if the base class only has protected constructors that require arguments, the derived class must provide them. This forces the derived class to explicitly communicate required dependencies.

A useful pattern is to have a protected parameterless constructor that assigns defaults and a protected constructor that accepts overrides. This gives derived classes flexibility while keeping the base class non-instantiable.

public class LoggerBase { protected LoggerBase() : this("debug") { } protected LoggerBase(string level) { Level = level; } public string Level { get; } } public class FileLogger : LoggerBase { public FileLogger() { } }

Here FileLogger can use the parameterless protected constructor from the base. The LoggerBase class cannot be instantiated directly, but both constructors are available to derived classes. This is a clean way to provide defaults without exposing construction to external code.

The compiler's handling of constructor initializers is important. A derived constructor always calls a base constructor, either explicitly with base(...), or implicitly the parameterless base constructor if one exists. If you want to avoid an implicit call to a public default constructor, defining a protected constructor without a parameter does not prevent that implicit call, because the protected parameterless constructor is still accessible to derived classes. To force derived classes to choose a specific base constructor, define only protected constructors that require arguments.

Practical Example: Repository Base Class

Let us tie these ideas together with a realistic example.

public abstract class Repository { protected Repository(IDatabase db, string collectionName) { if (db == null) throw new ArgumentNullException(nameof(db)); if (string.IsNullOrWhiteSpace(collectionName)) throw new ArgumentException("Collection name required.", nameof(collectionName)); Db = db; CollectionName = collectionName; } protected IDatabase Db { get; } protected string CollectionName { get; } public abstract Task<T> GetByIdAsync<T>(string id) where T : class; } public class MongoRepository : Repository { public MongoRepository(IDatabase db, string collectionName) : base(db, collectionName) { } public override async Task<T> GetByIdAsync<T>(string id) where T : class { // Implementation using Db and CollectionName await Task.CompletedTask; return default; } }

The protected constructor validates that dependencies are present before the derived class's constructor body runs. Every repository type must supply a database instance and a collection name. External code cannot create a Repository directly, which prevents incomplete objects.

This pattern keeps validation logic in one place, avoiding duplication across all derived repository classes. It also forces consistency in the way repositories are constructed throughout the codebase. The tradeoff is a slight inflexibility: if a repository needs different initialization logic, it must adapt to the base constructor signature or risk violating the base class contract.

Understanding c# protected constructor behavior is essential for building inheritance hierarchies that enforce construction rules. It offers a controlled middle ground between private constructors that block all external access and public constructors that allow any code to create instances. Choosing the right access level for a constructor is a design decision that affects testability, maintainability, and the overall object creation strategy. The key is to match the constructor's visibility to the class's intended role in the hierarchy.

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