Back to Blog
C#

C# Protected Constructor Usage and Scenarios

Protected constructors in C# control who can call a constructor: the containing class and derived classes. See syntax, inheritance behavior, factory usage, and common mistakes.

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

A protected constructor in C# limits direct instantiation to code inside the class itself and to derived classes. 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 initialized. It says: this class cannot be constructed directly as a base type; only code inside the class or derived type constructors may invoke its initialization.

A common use is a base class that provides shared logic but should not be constructed directly. A protected constructor blocks direct construction while still allowing derived classes to invoke the base initialization. On an abstract class, the abstract modifier already prevents direct instantiation, but a protected constructor also makes the intended construction contract explicit.

Consider a base class that centralizes configuration loading:

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

Here the protected constructor ensures every derived configuration type supplies a source path. The derived constructor body then calls Load() after the base has been initialized. 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 gets an implicit parameterless constructor. For a non-static, non-abstract class that constructor is public; for an abstract class it is protected. If you omit a constructor in a base class, derived classes can implicitly call the default parameterless constructor. That is different from defining a protected constructor, because an explicit protected constructor also suppresses the default constructor and can require arguments.

When you define an explicit protected constructor, you also suppress the default 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 { protected DatabaseConnection(string host, int port) { Host = host; Port = port; } public string Host { get; } public int Port { get; } public static DatabaseConnection Create(string host, int port) { return new DatabaseConnection(host, port); } }

External code calls DatabaseConnection.Create(...), while derived classes can still call the same constructor if they need to. If the class is not meant to be derived, a private constructor would be just as restrictive; choose protected when derived classes are expected to initialize the base.

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 from being constructed directly 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
privatecontaining class and nested typessingleton, immutable value creation, strict factory control
protectedcontaining class and derived typesabstract base initialization, template method pattern
publicany codenormal direct instantiation

A private constructor is not accessible to ordinary derived classes. 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 as a base class, you may need to change the constructor access to public. 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

For library scenarios, the protected internal modifier may be more appropriate than protected alone. It allows access from the same assembly as well as from derived classes in other assemblies. This is useful when you want to expose extensibility to external consumers while keeping the constructor internal for code inside the library.

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. A stricter approach, such as a private base constructor with nested derived types, can limit derivation, but it is a more intrusive design.

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 need a different design.

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 the parameterless base 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 protected constructors in C# is essential for building inheritance hierarchies that enforce construction rules. They offer 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.

Protected Constructors in C#: Usage and Examples | RYUSLOG DEV