Skip to content

Commit 0490d52

Browse files
BillWagnergewarren
andauthored
Update coding conventions for C# 13 (#44283)
* Fix small open issues. - #33341 already addressed in #36428. - Fixes #37294: Add text that the shortened format is valid only when the runtime type matches the variable type. - Fixes #37295: Don't use `ID` in the sample. - Fixes #37296: Fix nullable warnings. Other issue comments are incorrect. - Fixes #41748: Change the sample so the constructor is relevant. - Fixes #42858: Add an explanation on declaring variables in top level statements. * Remaining open issues - Fixes #43838: Add notes for collection expressions and using primary constructors. Include naming recommendations. - Fixes #43839 Update the overview of constructors to include information on primary constructors. * Code review for style Fix any style issues with the current code. * Final edit pass I also caught a couple sample bits that I'd neglected in the previous commit. * Apply suggestions from code review Co-authored-by: Genevieve Warren <[email protected]> --------- Co-authored-by: Genevieve Warren <[email protected]>
1 parent 462d811 commit 0490d52

File tree

10 files changed

+169
-138
lines changed

10 files changed

+169
-138
lines changed

docs/csharp/fundamentals/coding-style/coding-conventions.md

Lines changed: 46 additions & 23 deletions
Large diffs are not rendered by default.

docs/csharp/fundamentals/coding-style/snippets/coding-conventions/coding-conventions.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
<PropertyGroup>
44
<OutputType>WinExe</OutputType>
5-
<TargetFramework>net8.0-windows</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<TargetFramework>net9.0-windows</TargetFramework>
67
<Nullable>enable</Nullable>
78
<ImplicitUsings>enable</ImplicitUsings>
89
<StartupObject>Coding_Conventions_Examples.Program</StartupObject>

docs/csharp/fundamentals/coding-style/snippets/coding-conventions/program.cs

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,18 @@ static void Main(string[] args)
6161

6262
// Save snippet 4 and 5 for possible additions in program structure.
6363

64-
Name[] nameList = {new Name { FirstName = "Anderson", LastName = "Redmond" },
65-
new Name { FirstName = "Jones", LastName = "Seattle" },
66-
new Name { FirstName = "Anderson", LastName = "Redmond" }};
64+
Name[] nameList = [
65+
new Name { FirstName = "Anderson", LastName = "Redmond" },
66+
new Name { FirstName = "Jones", LastName = "Seattle" },
67+
new Name { FirstName = "Anderson", LastName = "Redmond" }
68+
];
6769
int n = 0;
6870

6971
//<snippet6>
7072
string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
7173
//</snippet6>
7274

73-
Console.WriteLine("{0}, {1}", nameList[n].LastName, nameList[n].FirstName);
75+
Console.WriteLine($"{nameList[n].LastName}, {nameList[n].FirstName}");
7476
Console.WriteLine(nameList[n].LastName + ", " + nameList[n].FirstName);
7577

7678
//<snippet7>
@@ -123,12 +125,9 @@ static void Main(string[] args)
123125
Console.WriteLine();
124126
//</snippet12>
125127

126-
//<snippet13a>
127-
string[] vowels1 = { "a", "e", "i", "o", "u" };
128-
//</snippet13a>
129-
//<snippet13b>
130-
var vowels2 = new string[] { "a", "e", "i", "o", "u" };
131-
//</snippet13b>
128+
//<snippet13>
129+
string[] vowels = [ "a", "e", "i", "o", "u" ];
130+
//</snippet13>
132131

133132
//<snippet15b>
134133
Del exampleDel2 = DelMethod;
@@ -152,10 +151,7 @@ static void Main(string[] args)
152151
}
153152
finally
154153
{
155-
if (bodyStyle != null)
156-
{
157-
((IDisposable)bodyStyle).Dispose();
158-
}
154+
bodyStyle?.Dispose();
159155
}
160156
//</snippet17a>
161157
//<snippet17b>
@@ -214,41 +210,35 @@ static void Main(string[] args)
214210

215211
ExampleClass.totalInstances = 1;
216212

217-
var customers = new List<Customer>
218-
{
219-
new Customer { Name = "Jones", ID = 432, City = "Redmond" }
220-
};
213+
List<Customer> Customers = [ new Customer { Name = "Jones", ID = 432, City = "Redmond" } ];
221214

222215
// Check shop name to use this.
223-
var distributors = new List<Distributor>
224-
{
225-
new Distributor { Name = "ShopSmart", ID = 11302, City = "Redmond" }
226-
};
216+
List<Distributor> Distributors = [ new Distributor { Name = "ShopSmart", ID = 11302, City = "Redmond" } ];
227217

228218
//<snippet25>
229219
//<snippet28>
230-
var seattleCustomers = from customer in customers
220+
var seattleCustomers = from customer in Customers
231221
//</snippet28>
232222
where customer.City == "Seattle"
233223
select customer.Name;
234224
//</snippet25>
235225

236226
//<snippet26>
237227
var localDistributors =
238-
from customer in customers
239-
join distributor in distributors on customer.City equals distributor.City
228+
from customer in Customers
229+
join distributor in Distributors on customer.City equals distributor.City
240230
select new { Customer = customer, Distributor = distributor };
241231
//</snippet26>
242232

243233
//<snippet27>
244234
var localDistributors2 =
245-
from customer in customers
246-
join distributor in distributors on customer.City equals distributor.City
247-
select new { CustomerName = customer.Name, DistributorID = distributor.ID };
235+
from customer in Customers
236+
join distributor in Distributors on customer.City equals distributor.City
237+
select new { CustomerName = customer.Name, DistributorName = distributor.Name };
248238
//</snippet27>
249239

250240
//<snippet29>
251-
var seattleCustomers2 = from customer in customers
241+
var seattleCustomers2 = from customer in Customers
252242
where customer.City == "Seattle"
253243
orderby customer.Name
254244
select customer;
@@ -257,13 +247,13 @@ orderby customer.Name
257247
// #30 is in class CompoundFrom
258248

259249
var customerDistributorNames =
260-
from customer in customers
261-
join distributor in distributors on customer.City equals distributor.City
250+
from customer in Customers
251+
join distributor in Distributors on customer.City equals distributor.City
262252
select new { CustomerName = customer.Name, DistributorID = distributor.ID };
263253

264254
var customerDistributorNames2 =
265-
from customer in customers
266-
from distributor in distributors
255+
from customer in Customers
256+
from distributor in Distributors
267257
where customer.City == distributor.City
268258
select new { CustomerName = customer.Name, DistributorID = distributor.ID };
269259

@@ -335,20 +325,14 @@ public static int IncrementTotal()
335325
return totalInstances;
336326
}
337327

338-
public static int ResultSoFar()
339-
{
340-
return 0;
341-
}
328+
public static int ResultSoFar() => 0;
342329
}
343330

344331
class BaseClass
345332
{
346333
protected static int totalInstances;
347334

348-
static BaseClass()
349-
{
350-
totalInstances = 0;
351-
}
335+
static BaseClass() => totalInstances = 0;
352336

353337
public static int IncrementTotal()
354338
{
@@ -367,40 +351,45 @@ class CompoundFrom
367351
public class Student
368352
{
369353
public string? LastName { get; set; }
370-
public List<int>? Scores { get; set; }
354+
public ICollection<int> Scores { get; set; } = default!;
371355
}
372356

373357
static void Main()
374358
{
375359

376360
// Use a collection initializer to create the data source. Note that
377361
// each element in the list contains an inner sequence of scores.
378-
List<Student> students = new List<Student>
379-
{
380-
new Student {LastName="Omelchenko", Scores= new List<int> {97, 72, 81, 60}},
381-
new Student {LastName="O'Donnell", Scores= new List<int> {75, 84, 91, 39}},
382-
new Student {LastName="Mortensen", Scores= new List<int> {88, 94, 65, 85}},
383-
new Student {LastName="Garcia", Scores= new List<int> {97, 89, 85, 82}},
384-
new Student {LastName="Beebe", Scores= new List<int> {35, 72, 91, 70}}
385-
};
362+
List<Student> students = [
363+
new Student {LastName="Omelchenko", Scores = [97, 72, 81, 60]},
364+
new Student {LastName="O'Donnell", Scores = [75, 84, 91, 39]},
365+
new Student {LastName="Mortensen", Scores = [88, 94, 65, 85]},
366+
new Student {LastName="Garcia", Scores = [97, 89, 85, 82]},
367+
new Student {LastName="Beebe", Scores = [35, 72, 91, 70]}
368+
];
386369

387370
//<snippet30>
388371
var scoreQuery = from student in students
389-
from score in student.Scores!
372+
from score in student.Scores
390373
where score > 90
391374
select new { Last = student.LastName, score };
392375
//</snippet30>
393376

377+
// <interpolatedStrings>
394378
// Execute the queries.
395379
Console.WriteLine("scoreQuery:");
396380
foreach (var student in scoreQuery)
397381
{
398-
Console.WriteLine("{0} Score: {1}", student.Last, student.score);
382+
Console.WriteLine($"{student.Last} Score: {student.score}");
399383
}
400-
401-
// Keep the console window open in debug mode.
402-
Console.WriteLine("Press any key to exit.");
403-
Console.ReadKey();
384+
// </interpolatedStrings>
385+
386+
// <rawStringLiterals>
387+
var message = """
388+
This is a long message that spans across multiple lines.
389+
It uses raw string literals. This means we can
390+
also include characters like \n and \t without escaping them.
391+
""";
392+
// </rawStringLiterals>
404393
}
405394
}
406395
}
@@ -459,7 +448,7 @@ public class List<T> { /*...*/ }
459448
//</TypeParametersOne>
460449

461450
//<TypeParametersTwo>
462-
public int IComparer<T>() { return 0; }
451+
public int IComparer<T>() => 0;
463452
public delegate bool Predicate<T>(T item);
464453
public struct Nullable<T> where T : struct { /*...*/ }
465454
//</TypeParametersTwo>
@@ -475,3 +464,22 @@ public interface ISessionChannel<TSession>
475464
}
476465
}//WrapParameters
477466
}
467+
468+
namespace Constructors
469+
{
470+
// <PrimaryRecord>
471+
public record Person(string FirstName, string LastName);
472+
// </PrimaryRecord>
473+
474+
// <PrimaryClass>
475+
public class LabelledContainer<T>(string label)
476+
{
477+
public string Label { get; } = label;
478+
public required T Contents
479+
{
480+
get;
481+
init;
482+
}
483+
}
484+
// </PrimaryClass>
485+
}
Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,46 @@
11
---
22
title: "Constructors"
33
description: A constructor in C# is called when a class or struct is created. Use constructors to set defaults, limit instantiation, and write flexible, easy-to-read code.
4-
ms.date: 04/06/2023
4+
ms.date: 01/15/2025
55
helpviewer_keywords:
66
- "constructors [C#]"
77
- "classes [C#], constructors"
88
- "C# language, constructors"
99
---
1010
# Constructors (C# programming guide)
1111

12-
Whenever an instance of a [class](../../language-reference/keywords/class.md) or a [struct](../../language-reference/builtin-types/struct.md) is created, its constructor is called. A class or struct may have multiple constructors that take different arguments. Constructors enable the programmer to set default values, limit instantiation, and write code that is flexible and easy to read. For more information and examples, see [Instance constructors](instance-constructors.md) and [Using constructors](using-constructors.md).
12+
A *constructor* is a method called by the runtime when an instance of a [class](../../language-reference/keywords/class.md) or a [struct](../../language-reference/builtin-types/struct.md) is created. A class or struct can have multiple constructors that take different arguments. Constructors enable you to ensure that instances of the type are valid when created. For more information and examples, see [Instance constructors](instance-constructors.md) and [Using constructors](using-constructors.md).
1313

14-
There are several actions that are part of initializing a new instance. Those actions take place in the following order:
14+
There are several actions that are part of initializing a new instance. The following actions take place in the following order:
1515

16-
1. *Instance fields are set to 0*. This is typically done by the runtime.
16+
1. *Instance fields are set to 0*. This initialization is typically done by the runtime.
1717
1. *Field initializers run*. The field initializers in the most derived type run.
1818
1. *Base type field initializers run*. Field initializers starting with the direct base through each base type to <xref:System.Object?displayProperty=fullName>.
1919
1. *Base instance constructors run*. Any instance constructors, starting with <xref:System.Object.%23ctor%2A?displayProperty=nameWithType> through each base class to the direct base class.
2020
1. *The instance constructor runs*. The instance constructor for the type runs.
21-
1. *Object initializers run*. If the expression includes any object initializers, those run after the instance constructor runs. Object initializers run in the textual order.
21+
1. *Object initializers run*. If the expression includes any object initializers, they run after the instance constructor runs. Object initializers run in the textual order.
2222

23-
The preceding actions take place when a new instance is initialized. If a new instance of a `struct` is set to its `default` value, all instance fields are set to 0.
23+
The preceding actions take place when an instance is created using the [`new` operator](../../language-reference//operators/new-operator.md). If a new instance of a `struct` is set to its `default` value, all instance fields are set to 0. Elements of an array are set to their default value of 0 or `null` when an array is created.
2424

25-
If the [static constructor](static-constructors.md) hasn't run, the static constructor runs before any of the instance constructor actions take place.
25+
The [static constructor](static-constructors.md), if any, runs before any of the instance constructor actions take place for any instance of the type. The static constructor runs at most once.
2626

2727
## Constructor syntax
2828

29-
A constructor is a method whose name is the same as the name of its type. Its method signature includes only an optional [access modifier](./access-modifiers.md), the method name and its parameter list; it does not include a return type. The following example shows the constructor for a class named `Person`.
29+
A constructor is a method with the same name as its type. Its method signature can include an optional [access modifier](./access-modifiers.md), the method name, and its parameter list; it doesn't include a return type. The following example shows the constructor for a class named `Person`.
3030

3131
:::code source="./snippets/constructors/Program.cs" id="InstanceCtor":::
3232

33-
If a constructor can be implemented as a single statement, you can use an [expression body definition](../statements-expressions-operators/expression-bodied-members.md). The following example defines a `Location` class whose constructor has a single string parameter named *name*. The expression body definition assigns the argument to the `locationName` field.
33+
If a constructor can be implemented as a single statement, you can use an [expression body member](../statements-expressions-operators/expression-bodied-members.md). The following example defines a `Location` class whose constructor has a single string parameter named *name*. The expression body definition assigns the argument to the `locationName` field.
3434

3535
:::code source="./snippets/constructors/Program.cs" id="ExpressionBodiedCtor":::
3636

37+
If a type requires a parameter to create an instance, you can use a *primary constructor* to indicate that one or more parameters are required to instantiate the type, as shown in the following example:
38+
39+
:::code source="./snippets/constructors/Program.cs" id="PrimaryCtor":::
40+
3741
## Static constructors
3842

39-
The previous examples have all shown instance constructors, which create a new object. A class or struct can also have a static constructor, which initializes static members of the type. Static constructors are parameterless. If you don't provide a static constructor to initialize static fields, the C# compiler initializes static fields to their default value as listed in the [Default values of C# types](../../language-reference/builtin-types/default-values.md) article.
43+
The previous examples show instance constructors, which initialize a new object. A class or struct can also declare a static constructor, which initializes static members of the type. Static constructors are parameterless. If you don't provide a static constructor to initialize static fields, the C# compiler initializes static fields to their default value as listed in the [Default values of C# types](../../language-reference/builtin-types/default-values.md) article.
4044

4145
The following example uses a static constructor to initialize a static field.
4246

@@ -48,17 +52,8 @@ You can also define a static constructor with an expression body definition, as
4852

4953
For more information and examples, see [Static Constructors](./static-constructors.md).
5054

51-
## In This Section
52-
53-
- [Using constructors](./using-constructors.md)
54-
- [Instance constructors](./instance-constructors.md)
55-
- [Private constructors](./private-constructors.md)
56-
- [Static constructors](./static-constructors.md)
57-
- [How to write a copy constructor](./how-to-write-a-copy-constructor.md)
58-
5955
## See also
6056

6157
- [The C# type system](../../fundamentals/types/index.md)
62-
- [Finalizers](./finalizers.md)
63-
- [static](../../language-reference/keywords/static.md)
58+
- [`static`](../../language-reference/keywords/static.md)
6459
- [Why Do Initializers Run In The Opposite Order As Constructors? Part One](/archive/blogs/ericlippert/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one)

docs/csharp/programming-guide/classes-and-structs/snippets/constructors/Program.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ public class Adult : Person
2525
public Adult(string lastName, string firstName) : base(lastName, firstName)
2626
{ }
2727

28-
static Adult()
29-
{
30-
minimumAge = 18;
31-
}
28+
static Adult() => minimumAge = 18;
3229

3330
// Remaining implementation of Adult class.
3431
}
@@ -63,3 +60,15 @@ public string Name
6360
}
6461
// </ExpressionBodiedCtor>
6562

63+
// <PrimaryCtor>
64+
public class LabelledContainer<T>(string label)
65+
{
66+
public string Label { get; } = label;
67+
public required T Contents
68+
{
69+
get;
70+
init;
71+
}
72+
}
73+
// </PrimaryCtor>
74+

docs/csharp/programming-guide/classes-and-structs/snippets/constructors/constructors.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net9.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>

0 commit comments

Comments
 (0)