DEV Community

Jairo Blanco
Jairo Blanco

Posted on

Are primary constructors good?

1. Understanding Primary Constructors

Primary constructors were originally introduced in C# alongside records as a way to define immutable data structures with minimal boilerplate. Since their expansion to regular classes in .NET 8, they have become a well-understood and widely adopted language feature. By the time of .NET 10, primary constructors are no longer considered experimental or novel—they are simply part of modern C#.

At a conceptual level, a primary constructor allows constructor parameters to be declared directly in the type definition. This reduces repetition, shortens class declarations, and makes the intent of a type immediately obvious.

This pattern is especially effective for simple data models, DTOs, and infrastructure types where the constructor exists primarily to initialize state rather than perform logic.


2. Primary Constructors in Classes

When using primary constructors in classes, their behavior still differs from records in a fundamental way. While records automatically expose their members, primary constructor parameters in classes remain private by default. Values passed through the constructor are not accessible outside the class unless explicitly exposed.

This design choice reinforces encapsulation but requires additional consideration when public access is required. Developers must be intentional about which values become part of the public surface area and which remain implementation details.

In scenarios where public, immutable data is the goal, this distinction continues to influence the choice between classes, record classes, and records—even in .NET 10.


3. Record Classes as an Alternative

Record classes remain a strong alternative when concise syntax and public accessibility are both desired. They combine the expressiveness of primary constructors with public members and init-only semantics.

In addition, record classes support value-based equality, making them particularly suitable for data transfer objects and domain models where identity is defined by data rather than reference.

Even in modern C# versions, record classes often eliminate the need for additional boilerplate when modeling immutable data structures.


4. Primary Constructors in Services

Primary constructors have proven especially valuable in service-oriented classes such as application services, use cases, handlers, and background processors.

These types typically exist for a narrow purpose: receiving dependencies via constructor injection and executing business logic. Traditional constructors often add noise without adding value. Primary constructors remove this overhead, allowing dependencies to be declared directly in the class signature.


5. Naming Conventions and Their Challenges

Despite the maturity of primary constructors, naming conventions remain the most debated aspect of their usage.

Traditional C# conventions clearly separate constructor parameters, private fields, and public members. Primary constructors collapse these distinctions, which can reduce visual clarity if conventions are not agreed upon.

Using PascalCase aligns visually with record syntax but conflicts with established parameter naming rules. Using camelCase follows IDE recommendations and long-standing conventions but removes the familiar visual cue that distinguishes injected dependencies.


6. Should You Use Primary Constructors?

In production environments, primary constructors are now a natural choice. They reduce boilerplate, improve readability, and lower long-term maintenance costs—especially in dependency-heavy code.

For educational material, the situation has also evolved. While older resources still exist, modern C# learning paths increasingly include primary constructors as a standard pattern rather than an advanced topic.

If starting a new project or creating up-to-date training material today, primary constructors are a sensible default—as long as their semantics, limitations, and conventions are clearly explained.

Top comments (0)