Skip to content

Commit

Permalink
Update coding conventions for C# 13 (#44283)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
BillWagner and gewarren authored Jan 17, 2025
1 parent 462d811 commit 0490d52
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 138 deletions.
69 changes: 46 additions & 23 deletions docs/csharp/fundamentals/coding-style/coding-conventions.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<StartupObject>Coding_Conventions_Examples.Program</StartupObject>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,18 @@ static void Main(string[] args)

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

Name[] nameList = {new Name { FirstName = "Anderson", LastName = "Redmond" },
new Name { FirstName = "Jones", LastName = "Seattle" },
new Name { FirstName = "Anderson", LastName = "Redmond" }};
Name[] nameList = [
new Name { FirstName = "Anderson", LastName = "Redmond" },
new Name { FirstName = "Jones", LastName = "Seattle" },
new Name { FirstName = "Anderson", LastName = "Redmond" }
];
int n = 0;

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

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

//<snippet7>
Expand Down Expand Up @@ -123,12 +125,9 @@ static void Main(string[] args)
Console.WriteLine();
//</snippet12>

//<snippet13a>
string[] vowels1 = { "a", "e", "i", "o", "u" };
//</snippet13a>
//<snippet13b>
var vowels2 = new string[] { "a", "e", "i", "o", "u" };
//</snippet13b>
//<snippet13>
string[] vowels = [ "a", "e", "i", "o", "u" ];
//</snippet13>

//<snippet15b>
Del exampleDel2 = DelMethod;
Expand All @@ -152,10 +151,7 @@ static void Main(string[] args)
}
finally
{
if (bodyStyle != null)
{
((IDisposable)bodyStyle).Dispose();
}
bodyStyle?.Dispose();
}
//</snippet17a>
//<snippet17b>
Expand Down Expand Up @@ -214,41 +210,35 @@ static void Main(string[] args)

ExampleClass.totalInstances = 1;

var customers = new List<Customer>
{
new Customer { Name = "Jones", ID = 432, City = "Redmond" }
};
List<Customer> Customers = [ new Customer { Name = "Jones", ID = 432, City = "Redmond" } ];

// Check shop name to use this.
var distributors = new List<Distributor>
{
new Distributor { Name = "ShopSmart", ID = 11302, City = "Redmond" }
};
List<Distributor> Distributors = [ new Distributor { Name = "ShopSmart", ID = 11302, City = "Redmond" } ];

//<snippet25>
//<snippet28>
var seattleCustomers = from customer in customers
var seattleCustomers = from customer in Customers
//</snippet28>
where customer.City == "Seattle"
select customer.Name;
//</snippet25>

//<snippet26>
var localDistributors =
from customer in customers
join distributor in distributors on customer.City equals distributor.City
from customer in Customers
join distributor in Distributors on customer.City equals distributor.City
select new { Customer = customer, Distributor = distributor };
//</snippet26>

//<snippet27>
var localDistributors2 =
from customer in customers
join distributor in distributors on customer.City equals distributor.City
select new { CustomerName = customer.Name, DistributorID = distributor.ID };
from customer in Customers
join distributor in Distributors on customer.City equals distributor.City
select new { CustomerName = customer.Name, DistributorName = distributor.Name };
//</snippet27>

//<snippet29>
var seattleCustomers2 = from customer in customers
var seattleCustomers2 = from customer in Customers
where customer.City == "Seattle"
orderby customer.Name
select customer;
Expand All @@ -257,13 +247,13 @@ orderby customer.Name
// #30 is in class CompoundFrom

var customerDistributorNames =
from customer in customers
join distributor in distributors on customer.City equals distributor.City
from customer in Customers
join distributor in Distributors on customer.City equals distributor.City
select new { CustomerName = customer.Name, DistributorID = distributor.ID };

var customerDistributorNames2 =
from customer in customers
from distributor in distributors
from customer in Customers
from distributor in Distributors
where customer.City == distributor.City
select new { CustomerName = customer.Name, DistributorID = distributor.ID };

Expand Down Expand Up @@ -335,20 +325,14 @@ public static int IncrementTotal()
return totalInstances;
}

public static int ResultSoFar()
{
return 0;
}
public static int ResultSoFar() => 0;
}

class BaseClass
{
protected static int totalInstances;

static BaseClass()
{
totalInstances = 0;
}
static BaseClass() => totalInstances = 0;

public static int IncrementTotal()
{
Expand All @@ -367,40 +351,45 @@ class CompoundFrom
public class Student
{
public string? LastName { get; set; }
public List<int>? Scores { get; set; }
public ICollection<int> Scores { get; set; } = default!;
}

static void Main()
{

// Use a collection initializer to create the data source. Note that
// each element in the list contains an inner sequence of scores.
List<Student> students = new List<Student>
{
new Student {LastName="Omelchenko", Scores= new List<int> {97, 72, 81, 60}},
new Student {LastName="O'Donnell", Scores= new List<int> {75, 84, 91, 39}},
new Student {LastName="Mortensen", Scores= new List<int> {88, 94, 65, 85}},
new Student {LastName="Garcia", Scores= new List<int> {97, 89, 85, 82}},
new Student {LastName="Beebe", Scores= new List<int> {35, 72, 91, 70}}
};
List<Student> students = [
new Student {LastName="Omelchenko", Scores = [97, 72, 81, 60]},
new Student {LastName="O'Donnell", Scores = [75, 84, 91, 39]},
new Student {LastName="Mortensen", Scores = [88, 94, 65, 85]},
new Student {LastName="Garcia", Scores = [97, 89, 85, 82]},
new Student {LastName="Beebe", Scores = [35, 72, 91, 70]}
];

//<snippet30>
var scoreQuery = from student in students
from score in student.Scores!
from score in student.Scores
where score > 90
select new { Last = student.LastName, score };
//</snippet30>

// <interpolatedStrings>
// Execute the queries.
Console.WriteLine("scoreQuery:");
foreach (var student in scoreQuery)
{
Console.WriteLine("{0} Score: {1}", student.Last, student.score);
Console.WriteLine($"{student.Last} Score: {student.score}");
}

// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
// </interpolatedStrings>

// <rawStringLiterals>
var message = """
This is a long message that spans across multiple lines.
It uses raw string literals. This means we can
also include characters like \n and \t without escaping them.
""";
// </rawStringLiterals>
}
}
}
Expand Down Expand Up @@ -459,7 +448,7 @@ public class List<T> { /*...*/ }
//</TypeParametersOne>

//<TypeParametersTwo>
public int IComparer<T>() { return 0; }
public int IComparer<T>() => 0;
public delegate bool Predicate<T>(T item);
public struct Nullable<T> where T : struct { /*...*/ }
//</TypeParametersTwo>
Expand All @@ -475,3 +464,22 @@ public interface ISessionChannel<TSession>
}
}//WrapParameters
}

namespace Constructors
{
// <PrimaryRecord>
public record Person(string FirstName, string LastName);
// </PrimaryRecord>

// <PrimaryClass>
public class LabelledContainer<T>(string label)
{
public string Label { get; } = label;
public required T Contents
{
get;
init;
}
}
// </PrimaryClass>
}
35 changes: 15 additions & 20 deletions docs/csharp/programming-guide/classes-and-structs/constructors.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,46 @@
---
title: "Constructors"
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.
ms.date: 04/06/2023
ms.date: 01/15/2025
helpviewer_keywords:
- "constructors [C#]"
- "classes [C#], constructors"
- "C# language, constructors"
---
# Constructors (C# programming guide)

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).
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).

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

1. *Instance fields are set to 0*. This is typically done by the runtime.
1. *Instance fields are set to 0*. This initialization is typically done by the runtime.
1. *Field initializers run*. The field initializers in the most derived type run.
1. *Base type field initializers run*. Field initializers starting with the direct base through each base type to <xref:System.Object?displayProperty=fullName>.
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.
1. *The instance constructor runs*. The instance constructor for the type runs.
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.
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.

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.
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.

If the [static constructor](static-constructors.md) hasn't run, the static constructor runs before any of the instance constructor actions take place.
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.

## Constructor syntax

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`.
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`.

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

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.
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.

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

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:

:::code source="./snippets/constructors/Program.cs" id="PrimaryCtor":::

## Static constructors

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.
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.

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

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

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

## In This Section

- [Using constructors](./using-constructors.md)
- [Instance constructors](./instance-constructors.md)
- [Private constructors](./private-constructors.md)
- [Static constructors](./static-constructors.md)
- [How to write a copy constructor](./how-to-write-a-copy-constructor.md)

## See also

- [The C# type system](../../fundamentals/types/index.md)
- [Finalizers](./finalizers.md)
- [static](../../language-reference/keywords/static.md)
- [`static`](../../language-reference/keywords/static.md)
- [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)
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ public class Adult : Person
public Adult(string lastName, string firstName) : base(lastName, firstName)
{ }

static Adult()
{
minimumAge = 18;
}
static Adult() => minimumAge = 18;

// Remaining implementation of Adult class.
}
Expand Down Expand Up @@ -63,3 +60,15 @@ public string Name
}
// </ExpressionBodiedCtor>

// <PrimaryCtor>
public class LabelledContainer<T>(string label)
{
public string Label { get; } = label;
public required T Contents
{
get;
init;
}
}
// </PrimaryCtor>

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Loading

0 comments on commit 0490d52

Please sign in to comment.