Skip to content

Commit 577b9f0

Browse files
Freshness Edit: dotnet content (#45370)
* refresh edits * edits * Apply suggestions from code review --------- Co-authored-by: Bill Wagner <[email protected]>
1 parent 624c702 commit 577b9f0

File tree

1 file changed

+51
-45
lines changed

1 file changed

+51
-45
lines changed
Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,76 @@
11
---
2-
title: Declare and use C# primary constructors in classes and structs
3-
description: "Learn how and when to declare primary constructors in your class and struct types. Primary constructors provide concise syntax to declare constructor parameters available anywhere in your type."
4-
ms.date: 05/26/2023
2+
title: Declare C# primary constructors – classes, structs
3+
description: Learn how and when to declare primary constructors in your class and struct types. Primary constructors provide concise syntax to declare constructor parameters available anywhere in the body of your type.
4+
ms.date: 03/18/2025
5+
ms.topic: how-to
6+
#customer intent: As a .NET developer, I want to declare and use C# primary constructors in classes and structs, so I can provide syntax to declare constructor parameters available anywhere in the type body.
57
---
6-
# Tutorial: Explore primary constructors
8+
# Declare primary constructors for classes and structs
79

8-
C# 12 introduces [*primary constructors*](../../programming-guide/classes-and-structs/instance-constructors.md#primary-constructors), a concise syntax to declare constructors whose parameters are available anywhere in the body of the type.
10+
C# 12 introduces [primary constructors](../../programming-guide/classes-and-structs/instance-constructors.md#primary-constructors), which provide a concise syntax to declare constructors whose parameters are available anywhere in the body of the type.
911

10-
In this tutorial, you will learn:
11-
12-
> [!div class="checklist"]
13-
>
14-
> - When to declare a primary constructor on your type
15-
> - How to call primary constructors from other constructors
16-
> - How to use primary constructor parameters in members of the type
17-
> - Where primary constructor parameters are stored
12+
This article describes how to declare a primary constructor on your type and recognize where to store primary constructor parameters. You can call primary constructors from other constructors and use primary constructor parameters in members of the type.
1813

1914
## Prerequisites
2015

2116
[!INCLUDE [Prerequisites](../../../../includes/prerequisites-basic.md)]
2217

23-
## Primary constructors
18+
## Understand rules for primary constructors
19+
20+
You can add parameters to a `struct` or `class` declaration to create a *primary constructor*. Primary constructor parameters are in scope throughout the class definition. It's important to view primary constructor parameters as *parameters* even though they are in scope throughout the class definition.
2421

25-
You can add parameters to a `struct` or `class` declaration to create a *primary constructor*. Primary constructor parameters are in scope throughout the class definition. It's important to view primary constructor parameters as *parameters* even though they are in scope throughout the class definition. Several rules clarify that they're parameters:
22+
Several rules clarify that these constructors are parameters:
2623

27-
1. Primary constructor parameters may not be stored if they aren't needed.
28-
1. Primary constructor parameters aren't members of the class. For example, a primary constructor parameter named `param` can't be accessed as `this.param`.
29-
1. Primary constructor parameters can be assigned to.
30-
1. Primary constructor parameters don't become properties, except in [`record`](../../language-reference/builtin-types/record.md) types.
24+
- Primary constructor parameters might not be stored if they aren't needed.
25+
- Primary constructor parameters aren't members of the class. For example, a primary constructor parameter named `param` can't be accessed as `this.param`.
26+
- Primary constructor parameters can be assigned to.
27+
- Primary constructor parameters don't become properties, except in [record](../../language-reference/builtin-types/record.md) types.
3128

32-
These rules are the same as parameters to any method, including other constructor declarations.
29+
These rules are the same rules already defined for parameters to any method, including other constructor declarations.
3330

34-
The most common uses for a primary constructor parameter are:
31+
Here are the most common uses for a primary constructor parameter:
3532

36-
1. As an argument to a `base()` constructor invocation.
37-
1. To initialize a member field or property.
38-
1. Referencing the constructor parameter in an instance member.
33+
- Pass as an argument to a `base()` constructor invocation
34+
- Initialize a member field or property
35+
- Reference the constructor parameter in an instance member
3936

40-
Every other constructor for a class **must** call the primary constructor, directly or indirectly, through a `this()` constructor invocation. That rule ensures that primary constructor parameters are assigned anywhere in the body of the type.
37+
Every other constructor for a class **must** call the primary constructor, directly or indirectly, through a `this()` constructor invocation. This rule ensures that primary constructor parameters are assigned everywhere in the body of the type.
4138

42-
## Initialize property
39+
## Initialize immutable properties or fields
4340

44-
The following code initializes two readonly properties that are computed from primary constructor parameters:
41+
The following code initializes two readonly (immutable) properties that are computed from primary constructor parameters:
4542

4643
:::code source="./snippets/primary-constructors/Distance.cs" id="ReadonlyStruct":::
4744

48-
The preceding code demonstrates a primary constructor used to initialize calculated readonly properties. The field initializers for `Magnitude` and `Direction` use the primary constructor parameters. The primary constructor parameters aren't used anywhere else in the struct. The preceding struct is as though you'd written the following code:
45+
This example uses a primary constructor to initialize calculated readonly properties. The field initializers for the `Magnitude` and `Direction` properties use the primary constructor parameters. The primary constructor parameters aren't used anywhere else in the struct. The code creates a struct as if it were written in the following manner:
4946

5047
:::code source="./snippets/primary-constructors/Distance.cs" id="StructOneLowered":::
5148

52-
The new feature makes it easier to use field initializers when you need arguments to initialize a field or property.
49+
This feature makes it easier to use field initializers when you need arguments to initialize a field or property.
5350

5451
## Create mutable state
5552

56-
The preceding examples use primary constructor parameters to initialize readonly properties. You can also use primary constructors when the properties aren't readonly. Consider the following code:
53+
The previous examples use primary constructor parameters to initialize readonly properties. You can also use primary constructors for properties that aren't readonly.
54+
55+
Consider the following code:
5756

5857
:::code source="./snippets/primary-constructors/Distance.cs" id="MutableStruct":::
5958

60-
In the preceding example, the `Translate` method changes the `dx` and `dy` components. That requires the `Magnitude` and `Direction` properties be computed when accessed. The `=>` operator designates an expression-bodied `get` accessor, whereas the `=` operator designates an initializer. This version adds a parameterless constructor to the struct. The parameterless constructor must invoke the primary constructor, so that all the primary constructor parameters are initialized.
59+
In this example, the `Translate` method changes the `dx` and `dy` components, which requires the `Magnitude` and `Direction` properties be computed when accessed. The greater than or equal to (`=>`) operator designates an expression-bodied `get` accessor, whereas the equal to (`=`) operator designates an initializer.
6160

62-
In the previous example, the primary constructor properties are accessed in a method. Therefore the compiler creates hidden fields to represent each parameter. The following code shows approximately what the compiler generates. The actual field names are valid CIL identifiers, but not valid C# identifiers.
61+
This version of the code adds a parameterless constructor to the struct. The parameterless constructor must invoke the primary constructor, which ensures all primary constructor parameters are initialized. The primary constructor properties are accessed in a method, and the compiler creates hidden fields to represent each parameter.
62+
63+
The following code demonstrates an approximation of what the compiler generates. The actual field names are valid Common Intermediate Language (CIL) identifiers, but not valid C# identifiers.
6364

6465
:::code source="./snippets/primary-constructors/Distance.cs" id="StructTwoLowered":::
6566

66-
It's important to understand that the first example didn't require the compiler to create a field to store the value of the primary constructor parameters. The second example used the primary constructor parameter inside a method, and therefore required the compiler to create storage for them. The compiler creates storage for any primary constructors only when that parameter is accessed in the body of a member of your type. Otherwise, the primary constructor parameters aren't stored in the object.
67+
### Compiler-created storage
68+
69+
For the first example in this section, the compiler didn't need to create a field to store the value of the primary constructor parameters. However, in the second example, the primary constructor parameter is used inside a method, so the compiler must create storage for the parameters.
70+
71+
The compiler creates storage for any primary constructors only when the parameter is accessed in the body of a member of your type. Otherwise, the primary constructor parameters aren't stored in the object.
6772

68-
## Dependency injection
73+
## Use dependency injection
6974

7075
Another common use for primary constructors is to specify parameters for dependency injection. The following code creates a simple controller that requires a service interface for its use:
7176

@@ -75,34 +80,35 @@ The primary constructor clearly indicates the parameters needed in the class. Yo
7580

7681
## Initialize base class
7782

78-
You can invoke a base class' primary constructor from the derived class' primary constructor. It's the easiest way for you to write a derived class that must invoke a primary constructor in the base class. For example, consider a hierarchy of classes that represent different account types as a bank. The base class would look something like the following code:
83+
You can invoke the primary constructor for a base class from the primary constructor of derived class. This approach is the easiest way to write a derived class that must invoke a primary constructor in the base class. Consider a hierarchy of classes that represent different account types as a bank. The following code shows what the base class might look like:
7984

8085
:::code source="./snippets/primary-constructors/BankAccount.cs" id="BaseClass":::
8186

82-
All bank accounts, regardless of the type, have properties for the account number and an owner. In the completed application, other common functionality would be added to the base class.
87+
All bank accounts, regardless of the type, have properties for the account number and owner. In the completed application, you can add other common functionality to the base class.
8388

84-
Many types require more specific validation on constructor parameters. For example, the `BankAccount` has specific requirements for the `owner` and `accountID` parameters: The `owner` must not be `null` or whitespace, and the `accountID` must be a string containing 10 digits. You can add this validation when you assign the corresponding properties:
89+
Many types require more specific validation on constructor parameters. For example, the `BankAccount` class has specific requirements for the `owner` and `accountID` parameters. The `owner` parameter must not be `null` or whitespace, and the `accountID` parameter must be a string containing 10 digits. You can add this validation when you assign the corresponding properties:
8590

8691
:::code source="./snippets/primary-constructors/BankAccountValidation.cs" id="BaseClassValidation":::
8792

88-
The previous example shows how you can validate the constructor parameters before assigning them to the properties. You can use builtin methods, like <xref:System.String.IsNullOrWhiteSpace(System.String)?displayProperty=nameWithType>, or your own validation method, like `ValidAccountNumber`. In the previous example, any exceptions are thrown from the constructor, when it invokes the initializers. If a constructor parameter isn't used to assign a field, any exceptions are thrown when the constructor parameter is first accessed.
93+
This example shows how to validate the constructor parameters before you assign them to the properties. You can use built-in methods like <xref:System.String.IsNullOrWhiteSpace(System.String)?displayProperty=nameWithType> or your own validation method, such as `ValidAccountNumber`. In the example, any exceptions are thrown from the constructor, when it invokes the initializers. If a constructor parameter isn't used to assign a field, any exceptions are thrown when the constructor parameter is first accessed.
8994

90-
One derived class would present a checking account:
95+
One derived class might represent a checking account:
9196

9297
:::code source="./snippets/primary-constructors/BankAccount.cs" id="DerivedClass":::
9398

94-
The derived `CheckingAccount` class has a primary constructor that takes all the parameters needed in the base class, and another parameter with a default value. The primary constructor calls the base constructor using the `: BankAccount(accountID, owner)` syntax. This expression specifies both the type for the base class, and the arguments for the primary constructor.
99+
The derived `CheckingAccount` class has a primary constructor that takes all the parameters needed in the base class, and another parameter with a default value. The primary constructor calls the base constructor with the `: BankAccount(accountID, owner)` syntax. This expression specifies both the type for the base class and the arguments for the primary constructor.
95100

96-
Your derived class isn't required to use a primary constructor. You can create a constructor in the derived class that invokes the base class' primary constructor, as shown in the following example:
101+
Your derived class isn't required to use a primary constructor. You can create a constructor in the derived class that invokes the primary constructor for the base class, as shown in the following example:
97102

98103
:::code source="./snippets/primary-constructors/BankAccount.cs" id="NoPrimaryConstructor":::
99104

100-
There's one potential concern with class hierarchies and primary constructors: it's possible to create multiple copies of a primary constructor parameter as it's used in both derived and base classes. The following code example creates two copies each of the `owner` and `accountID` field:
105+
There's one potential concern with class hierarchies and primary constructors. It's possible to create multiple copies of a primary constructor parameter because the parameter is used in both derived and base classes. The following code creates two copies each of the `owner` and `accountID` parameters:
101106

102107
:::code source="./snippets/primary-constructors/BankAccount.cs" id="DuplicatedPrimaryConstructorStorage" highlight="33":::
103108

104-
The highlighted line shows that the `ToString` method uses the *primary constructor parameters* (`owner` and `accountID`) rather than the *base class properties* (`Owner` and `AccountID`). The result is that the derived class, `SavingsAccount` creates storage for those copies. The copy in the derived class is different than the property in the base class. If the base class property could be modified, the instance of the derived class won't see that modification. The compiler issues a warning for primary constructor parameters that are used in a derived class and passed to a base class constructor. In this instance, the fix is to use the properties of the base class.
109+
The highlighted line in this example shows that the `ToString` method uses the *primary constructor parameters* (`owner` and `accountID`) rather than the *base class properties* (`Owner` and `AccountID`). The result is that the derived class, `SavingsAccount`, creates storage for the parameter copies. The copy in the derived class is different than the property in the base class. If the base class property can be modified, the instance of the derived class doesn't see the modification. The compiler issues a warning for primary constructor parameters that are used in a derived class and passed to a base class constructor. In this instance, the fix is to use the properties of the base class.
105110

106-
## Summary
111+
## Related content
107112

108-
You can use the primary constructors as best suits your design. For classes and structs, primary constructor parameters are parameters to a constructor that must be invoked. You can use them to initialize properties. You can initialize fields. Those properties or fields can be immutable, or mutable. You can use them in methods. They're parameters, and you use them in what manner suits your design best. You can learn more about primary constructors in the [C# programming guide article on instance constructors](../../programming-guide/classes-and-structs/instance-constructors.md#parameterless-constructors) and the [proposed primary constructor specification](~/_csharplang/proposals/csharp-12.0/primary-constructors.md).
113+
- [Parameterless constructors (C# programming guide)](../../programming-guide/classes-and-structs/instance-constructors.md#parameterless-constructors)
114+
- [Primary constructors (Feature specification proposal)](~/_csharplang/proposals/csharp-12.0/primary-constructors.md)

0 commit comments

Comments
 (0)