Home C# / .NET Primary Constructors & Required Members in C# 12
Intermediate 6 min · July 13, 2026

Primary Constructors & Required Members in C# 12

Master C# 12 primary constructors and required members with practical examples.

N
Naren Founder & Principal Engineer

20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 13, 2026
last updated
2,043
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic knowledge of C# classes and constructors
  • Familiarity with object initializers
  • Understanding of properties and fields
  • Visual Studio 2022 or .NET 8 SDK installed
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Primary constructors allow you to declare constructor parameters directly in the class declaration, reducing boilerplate.
  • Required members enforce that certain properties or fields are set during object initialization.
  • Both features work together to create immutable, self-documenting types.
  • Primary constructors are available for classes, structs, and records.
  • Required members can be used with object initializers and are validated at compile time.
✦ Definition~90s read
What is Primary Constructors and Required Members?

Primary constructors and required members are C# 12 features that let you declare constructor parameters directly in the class header and enforce that certain properties are initialized, making your code more concise and safer.

Imagine you're ordering a custom pizza.
Plain-English First

Imagine you're ordering a custom pizza. The primary constructor is like saying 'I want a pizza with cheese and pepperoni' right when you order, instead of first saying 'I want a pizza' and then adding toppings later. Required members are like mandatory fields on a form – you can't submit without filling them in. Together, they make sure your pizza is exactly what you wanted, with no missing ingredients.

C# 12 introduces two powerful features that streamline object creation and enforce initialization contracts: primary constructors and required members. These features are part of the ongoing effort to make C# more expressive and less verbose, especially when working with immutable data and dependency injection.

Primary constructors allow you to declare constructor parameters directly in the class declaration, eliminating the need for explicit constructor bodies and field assignments. This is particularly useful for simple types that primarily hold data or have straightforward initialization logic. Required members, on the other hand, ensure that certain properties or fields are set during object initialization, either via constructor parameters or object initializers. This prevents the common bug of forgetting to initialize a critical member.

Together, they enable a more declarative style of programming where the structure of your class communicates its initialization requirements. In this tutorial, we'll explore both features in depth, with practical examples, common pitfalls, and production-ready patterns. By the end, you'll be able to write cleaner, safer C# code that leverages the full power of .NET 8.

Understanding Primary Constructors

Primary constructors allow you to declare constructor parameters directly in the class declaration. This feature is available for classes, structs, and records. The parameters are in scope throughout the class body, meaning you can use them to initialize fields, properties, or even pass to base constructors.

```csharp public class Person { public string FirstName { get; } public string LastName { get; }

public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } } ```

``csharp public class Person(string firstName, string lastName) { public string FirstName { get; } = firstName; public string LastName { get; } = lastName; } ``

The parameters firstName and lastName are available throughout the class. They are captured and can be used in property initializers, method bodies, etc. However, they are not automatically stored as fields; you must explicitly assign them if you want to keep them.

Primary constructors are especially useful for simple data holders and types that require dependency injection. They reduce boilerplate and make the class's dependencies explicit.

PrimaryConstructorExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
public class Person(string firstName, string lastName)
{
    public string FirstName { get; } = firstName;
    public string LastName { get; } = lastName;
    public string FullName => $"{FirstName} {LastName}";
}

// Usage
var person = new Person("John", "Doe");
Console.WriteLine(person.FullName); // John Doe
Output
John Doe
💡Primary Constructor Parameters Are Not Fields
📊 Production Insight
In production, primary constructors are great for simple DTOs and service classes where dependencies are injected. However, be cautious with mutable parameters; prefer capturing them in readonly fields.
🎯 Key Takeaway
Primary constructors reduce boilerplate by allowing you to declare constructor parameters directly in the class declaration.

Required Members: Enforcing Initialization

Required members are a new feature that allows you to mark properties or fields as mandatory for initialization. This means that any code creating an instance of the class must set those members, either via constructor or object initializer. If a required member is not set, the compiler emits an error.

To declare a required member, use the required modifier on a property or field. Required members can be used in classes, structs, and records. They work well with object initializers and constructors.

```csharp public class Product { public required string Name { get; init; } public required decimal Price { get; init; } public string? Description { get; init; } }

// Usage var product = new Product { Name = "Laptop", Price = 999.99m }; // Description is optional ```

If you forget to set Name or Price, you'll get a compilation error. This is a powerful way to enforce initialization contracts without writing custom constructors.

Required members can also be combined with primary constructors. For example:

``csharp public class Order(int orderId) : IOrder { public required int OrderId { get; init; } = orderId; public required DateTime OrderDate { get; init; } } ``

Here, OrderId is set from the primary constructor parameter, but OrderDate must be set via object initializer.

RequiredMemberExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
public class Employee
{
    public required string Name { get; init; }
    public required int Id { get; init; }
    public string? Department { get; init; }
}

// Correct usage
var emp = new Employee { Name = "Alice", Id = 1 };

// Compilation error: Required member 'Id' must be set
// var emp2 = new Employee { Name = "Bob" };
⚠ Required Members and Inheritance
📊 Production Insight
Use required members for configuration objects, DTOs, and any type where missing initialization could lead to runtime errors. They are especially useful in large codebases where object creation is scattered.
🎯 Key Takeaway
Required members enforce that certain properties are initialized, preventing null reference errors and making initialization contracts explicit.

Combining Primary Constructors and Required Members

Primary constructors and required members can be used together to create concise yet robust initialization patterns. The primary constructor parameters can be used to set required members, and additional required members can be set via object initializers.

Consider a scenario where you have a base class with a primary constructor and a derived class that adds required members:

```csharp public class Person(string name) { public required string Name { get; init; } = name; }

public class Employee(string name, int id) : Person(name) { public required int Id { get; init; } = id; }

// Usage var emp = new Employee("Alice", 1); // Both Name and Id are set via primary constructor parameters ```

In this example, the primary constructor parameter name is used to initialize the required property Name. The derived class's primary constructor passes name to the base class and also initializes Id. This pattern ensures that all required members are set without needing explicit constructor bodies.

However, there is a nuance: when you use a primary constructor parameter to initialize a required member, you must ensure that the parameter is captured. In the above example, name is captured because it's used in the property initializer. If you don't use it, you'll get a warning.

Another pattern is to have required members that are not set by the primary constructor but must be set via object initializer. This is useful when you have some mandatory parameters and some optional ones that are still required.

```csharp public class Order(int orderId) { public required int OrderId { get; init; } = orderId; public required DateTime OrderDate { get; init; } }

// Usage var order = new Order(123) { OrderDate = DateTime.UtcNow }; ```

Here, OrderId is set from the constructor, but OrderDate must be set in the object initializer.

CombinedExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
public class Product(string name, decimal price)
{
    public required string Name { get; init; } = name;
    public required decimal Price { get; init; } = price;
    public string? Description { get; init; }
}

// Usage
var product = new Product("Laptop", 999.99m) { Description = "High-end laptop" };
Console.WriteLine($"{product.Name}: {product.Price:C}");
Output
Laptop: $999.99
🔥Order of Initialization
📊 Production Insight
In production, this combination is ideal for immutable types where you want to enforce that all essential data is provided at creation time. It reduces the chance of incomplete object state.
🎯 Key Takeaway
Combining primary constructors with required members allows you to create classes that are both concise and safe, with clear initialization requirements.

Required Members in Interfaces and Abstract Classes

Required members can also be used in interfaces and abstract classes to enforce that implementing or derived classes provide certain properties. This is a powerful way to define contracts that include initialization requirements.

For interfaces, you can declare required properties without a body. Any class implementing the interface must then provide those required members.

```csharp public interface IConfigurable { required string ConnectionString { get; init; } required int TimeoutSeconds { get; init; } }

public class DatabaseConfig : IConfigurable { public required string ConnectionString { get; init; } public required int TimeoutSeconds { get; init; } }

// Usage var config = new DatabaseConfig { ConnectionString = "Server=...", TimeoutSeconds = 30 }; ```

In abstract classes, you can mark abstract or virtual properties as required. Derived classes must then override them and mark them as required as well.

```csharp public abstract class Vehicle { public abstract required string Model { get; init; } public abstract required int Year { get; init; } }

public class Car : Vehicle { public override required string Model { get; init; } public override required int Year { get; init; } } ```

Note that when overriding a required member, the override must also be marked as required. This ensures that the initialization contract is preserved.

InterfaceRequiredExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IEntity
{
    required int Id { get; init; }
}

public class User : IEntity
{
    public required int Id { get; init; }
    public required string Name { get; init; }
}

// Usage
var user = new User { Id = 1, Name = "Alice" };
💡Required Members in Interfaces
📊 Production Insight
Use required members in interfaces for configuration and data contracts. This ensures that all implementations properly initialize essential properties, reducing runtime errors.
🎯 Key Takeaway
Required members in interfaces and abstract classes enforce initialization contracts across type hierarchies.

Common Pitfalls and Best Practices

While primary constructors and required members are powerful, they come with some pitfalls. Understanding these will help you avoid bugs and write cleaner code.

Pitfall 1: Accidentally Capturing Parameters

Primary constructor parameters are captured if used in the class body. If you don't intend to store them, be careful not to use them inadvertently. For example:

``csharp public class Logger(string category) { public void Log(string message) { Console.WriteLine($"[{category}] {message}"); // Captures category } } ``

Here, category is captured because it's used in the Log method. If you only need it for initialization, consider using a local variable or a field.

Pitfall 2: Mutable Primary Constructor Parameters

If you capture a primary constructor parameter in a mutable field, you can change it later, which might break immutability. Prefer capturing in readonly fields or init-only properties.

Pitfall 3: Required Members with Default Values

Required members cannot have default values because they must be explicitly set. If you need a default, consider making the member optional (not required) and providing a default in the constructor.

Best Practices:

  • Use primary constructors for simple types that primarily hold data.
  • Use required members for properties that are essential for the object's validity.
  • Combine them for immutable types with clear initialization contracts.
  • Avoid using primary constructors for classes with complex initialization logic; use explicit constructors instead.
  • Document required members with XML comments to inform consumers.
PitfallExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Bad: Mutable capture
public class BadExample(string name)
{
    public string Name { get; set; } = name; // Can be changed later
}

// Good: Immutable capture
public class GoodExample(string name)
{
    public string Name { get; init; } = name;
}

// Usage
var good = new GoodExample("Alice");
// good.Name = "Bob"; // Error: init-only setter
⚠ Avoid Mutable Captures
📊 Production Insight
In production, enforce immutability by using init-only properties and readonly fields. This prevents accidental modifications and makes your code easier to reason about.
🎯 Key Takeaway
Be mindful of parameter capture and mutability when using primary constructors. Use required members to enforce initialization contracts.

Real-World Example: Configuration Object

Let's build a real-world configuration object that uses both primary constructors and required members. This is a common scenario in .NET applications where you need to load settings from appsettings.json and enforce that all required settings are present.

Consider an application that connects to a database and an external API. The configuration class might look like this:

``csharp public class AppConfig(string environment) { public required string Environment { get; init; } = environment; public required string DatabaseConnectionString { get; init; } public required string ApiEndpoint { get; init; } public int TimeoutSeconds { get; init; } = 30; } ``

Here, Environment is set from the primary constructor parameter, but DatabaseConnectionString and ApiEndpoint are required and must be set via object initializer. TimeoutSeconds has a default value and is optional.

When loading from configuration, you can use the options pattern:

```csharp // In Program.cs var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<AppConfig>(options => { var env = builder.Environment.EnvironmentName; // Create instance with primary constructor and object initializer var config = new AppConfig(env) { DatabaseConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"), ApiEndpoint = builder.Configuration["ApiEndpoint"] }; // Copy to options options.Environment = config.Environment; options.DatabaseConnectionString = config.DatabaseConnectionString; options.ApiEndpoint = config.ApiEndpoint; options.TimeoutSeconds = config.TimeoutSeconds; }); ```

This pattern ensures that all required configuration values are provided at startup. If any are missing, you'll get a compile-time error if you use the required members correctly.

Alternatively, you can use the [Required] data annotation, but that only works at runtime. Required members give you compile-time safety.

AppConfigExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AppConfig(string environment)
{
    public required string Environment { get; init; } = environment;
    public required string DatabaseConnectionString { get; init; }
    public required string ApiEndpoint { get; init; }
    public int TimeoutSeconds { get; init; } = 30;
}

// Usage
var config = new AppConfig("Production")
{
    DatabaseConnectionString = "Server=...",
    ApiEndpoint = "https://api.example.com"
};
Console.WriteLine(config.Environment); // Production
Output
Production
🔥Configuration Validation
📊 Production Insight
In production, this pattern prevents misconfigured applications from starting. It's especially useful in microservices where configuration is critical.
🎯 Key Takeaway
Use primary constructors and required members for configuration objects to ensure all essential settings are provided at initialization.
● Production incidentPOST-MORTEMseverity: high

The Missing Initialization Bug

Symptom
Users reported intermittent crashes with NullReferenceException when accessing a configuration object.
Assumption
The developer assumed all properties were initialized in the constructor.
Root cause
A new required property was added but not initialized in all object creation paths.
Fix
Changed the property to a required member, forcing initialization at compile time.
Key lesson
  • Use required members for properties that must be set during initialization.
  • Avoid relying on implicit initialization; be explicit about what's required.
  • Leverage compiler checks to catch missing initializations early.
  • Document initialization contracts with required members.
  • Consider using primary constructors to reduce boilerplate and improve clarity.
Production debug guideSymptom to Action3 entries
Symptom · 01
Compilation error: 'Required member must be set'
Fix
Ensure all required properties are initialized in object initializer or constructor.
Symptom · 02
NullReferenceException on a property that should be set
Fix
Check if the property is required; if not, consider making it required or adding null checks.
Symptom · 03
Unexpected behavior due to mutable primary constructor parameter
Fix
Capture the parameter in a readonly field or use a record for immutability.
★ Quick Debug Cheat SheetCommon issues and immediate fixes for primary constructors and required members.
CS9035: Required member 'X' must be set
Immediate action
Initialize 'X' in the object initializer or constructor.
Commands
Add 'X = value;' in constructor or use 'new MyClass { X = value }'
Check if property is marked 'required' and you are using an object initializer.
Fix now
Set the property in all creation paths.
CS9113: Parameter 'param' is captured but not used+
Immediate action
Use the parameter in the class body or remove it.
Commands
Assign it to a field or property.
If not needed, remove from primary constructor.
Fix now
Add a field or property that uses the parameter.
Property setter is inaccessible due to init-only+
Immediate action
Use object initializer or constructor to set the property.
Commands
new MyClass { Prop = value }
If using primary constructor, capture parameter and assign in constructor.
Fix now
Change setter to 'init' or 'set' if appropriate.
FeaturePrimary ConstructorExplicit ConstructorRequired Member
DeclarationIn class headerInside class bodyModifier on property/field
BoilerplateMinimalMore verboseN/A
Logic supportNone (capture only)Full logicN/A
InheritanceSupported via base()SupportedSupported
Compile-time safetyNoNoYes
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
PrimaryConstructorExample.cspublic class Person(string firstName, string lastName)Understanding Primary Constructors
RequiredMemberExample.cspublic class EmployeeRequired Members
CombinedExample.cspublic class Product(string name, decimal price)Combining Primary Constructors and Required Members
InterfaceRequiredExample.cspublic interface IEntityRequired Members in Interfaces and Abstract Classes
PitfallExample.cspublic class BadExample(string name)Common Pitfalls and Best Practices
AppConfigExample.cspublic class AppConfig(string environment)Real-World Example

Key takeaways

1
Primary constructors reduce boilerplate by allowing constructor parameters directly in the class declaration.
2
Required members enforce initialization contracts at compile time, preventing null reference errors.
3
Combine both features for concise, immutable types with clear initialization requirements.
4
Use primary constructors for simple data holders and dependency injection; prefer explicit constructors for complex logic.
5
Required members work with interfaces, abstract classes, and records, providing consistent initialization contracts.

Common mistakes to avoid

3 patterns
×

Forgetting to capture a primary constructor parameter in a field or property.

×

Using a mutable property to capture a primary constructor parameter, breaking immutability.

×

Not marking a property as required when it must be set for the object to be valid.

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the difference between a primary constructor and an explicit con...
Q02JUNIOR
How do required members improve code safety?
Q03SENIOR
Can you combine primary constructors with required members? Provide an e...
Q04SENIOR
What are the limitations of primary constructors?
Q01 of 04SENIOR

Explain the difference between a primary constructor and an explicit constructor in C# 12.

ANSWER
A primary constructor is declared directly in the class declaration, and its parameters are in scope throughout the class body. An explicit constructor is a separate method inside the class. Primary constructors reduce boilerplate but cannot have additional logic beyond parameter capture. Explicit constructors allow full control over initialization logic.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Can I use primary constructors with inheritance?
02
Are required members supported in structs?
03
Can I have both a primary constructor and an explicit constructor?
04
What happens if I don't set a required member?
05
Can required members be used with records?
N
Naren Founder & Principal Engineer

20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 13, 2026
last updated
2,043
articles · all by Naren
🔥

That's C# Advanced. Mark it forged?

6 min read · try the examples if you haven't

Previous
Collection Expressions and Frozen Collections
19 / 22 · C# Advanced
Next
BenchmarkDotNet for .NET Performance