From b74766942f2d9c52375d29c515be9fdcaced4d4b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 1 Aug 2024 13:45:05 -0400 Subject: [PATCH] Miscellaneous bug fixes (#41967) * Fixes #41491 Clarified the diamond problem with default interface methods. Add an image to illustrate the problem, and provide additional text explanation. * Fixes #40808 Remove the (now) obsolete paragraph. * Fixes #41507 Clarify how extra braces are processed in interpolated raw string literals. * Fixes #41538 Add collection expressions to the member access article. Include links to the collection expressions article. * Fixes #30876 Add important note that an instance constructor called from a static field initializer in the same type that invokes an instance constructor means the instance constructor is called before the static constructor. * Fixes #41629 Note that the order of static constructor execution isn't specified. Perform a final edit pass. * build fix * Update docs/csharp/programming-guide/strings/index.md --- .../diamond-problem.svg | 1 + .../mixins-with-default-interface-methods.md | 22 +++++++----- .../operators/member-access-operators.md | 10 +++--- .../language-reference/tokens/interpolated.md | 18 +++++----- .../snippets/static-constructors/Program.cs | 21 ++++++++++++ .../static-constructors.md | 34 +++++++++++-------- .../csharp/programming-guide/strings/index.md | 16 ++++----- 7 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 docs/csharp/advanced-topics/interface-implementation/media/mixins-with-default-interface-methods/diamond-problem.svg diff --git a/docs/csharp/advanced-topics/interface-implementation/media/mixins-with-default-interface-methods/diamond-problem.svg b/docs/csharp/advanced-topics/interface-implementation/media/mixins-with-default-interface-methods/diamond-problem.svg new file mode 100644 index 0000000000000..02fceb3493b0f --- /dev/null +++ b/docs/csharp/advanced-topics/interface-implementation/media/mixins-with-default-interface-methods/diamond-problem.svg @@ -0,0 +1 @@ +
« interface »
ILight
PowerStatus
« interface »
IBlinkingLight
PowerStatus
« interface »
ITimerLight
PowerStatus
OverheadLight
\ No newline at end of file diff --git a/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md b/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md index 075304f167687..e1dded0371fd0 100644 --- a/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md +++ b/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md @@ -1,13 +1,13 @@ --- title: Create mixin types using default interface methods description: Using default interface members you can extend interfaces with optional default implementations for implementors. -ms.date: 03/17/2023 +ms.date: 07/31/2024 --- # Tutorial: Mix functionality in when creating classes using interfaces with default interface methods You can define an implementation when you declare a member of an interface. This feature provides new capabilities where you can define default implementations for features declared in interfaces. Classes can pick when to override functionality, when to use the default functionality, and when not to declare support for discrete features. -In this tutorial, you'll learn how to: +In this tutorial, you learn how to: > [!div class="checklist"] > @@ -27,11 +27,11 @@ Extension methods are resolved at compile time, using the declared type of the v You can declare the default implementations as interface methods. Then, every class automatically uses the default implementation. Any class that can provide a better implementation can override the interface method definition with a better algorithm. In one sense, this technique sounds similar to how you could use [extension methods](../../programming-guide/classes-and-structs/extension-methods.md). -In this article, you'll learn how default interface implementations enable new scenarios. +In this article, you learn how default interface implementations enable new scenarios. ## Design the application -Consider a home automation application. You probably have many different types of lights and indicators that could be used throughout the house. Every light must support APIs to turn them on and off, and to report the current state. Some lights and indicators may support other features, such as: +Consider a home automation application. You probably have many different types of lights and indicators that could be used throughout the house. Every light must support APIs to turn them on and off, and to report the current state. Some lights and indicators might support other features, such as: - Turn light on, then turn it off after a timer. - Blink the light for a period of time. @@ -68,7 +68,7 @@ The `OverheadLight` class can implement the timer function by declaring support public class OverheadLight : ITimerLight { } ``` -A different light type may support a more sophisticated protocol. It can provide its own implementation for `TurnOnFor`, as shown in the following code: +A different light type might support a more sophisticated protocol. It can provide its own implementation for `TurnOnFor`, as shown in the following code: :::code language="csharp" source="./snippets/mixins-with-default-interface-methods/HalogenLight.cs" id="SnippetHalogenLight"::: @@ -96,7 +96,7 @@ The `HalogenLight` you created earlier doesn't support blinking. So, don't add t ## Detect the light types using pattern matching -Next, let's write some test code. You can make use of C#'s [pattern matching](../../fundamentals/functional/pattern-matching.md) feature to determine a light's capabilities by examining which interfaces it supports. The following method exercises the supported capabilities of each light: +Next, let's write some test code. You can make use of C#'s [pattern matching](../../fundamentals/functional/pattern-matching.md) feature to determine a light's capabilities by examining which interfaces it supports. The following method exercises the supported capabilities of each light: :::code language="csharp" source="./snippets/mixins-with-default-interface-methods/Program.cs" id="SnippetTestLightFunctions"::: @@ -116,6 +116,12 @@ The default implementation assumes no power: These changes compile cleanly, even though the `ExtraFancyLight` declares support for the `ILight` interface and both derived interfaces, `ITimerLight` and `IBlinkingLight`. There's only one "closest" implementation declared in the `ILight` interface. Any class that declared an override would become the one "closest" implementation. You saw examples in the preceding classes that overrode the members of other derived interfaces. -Avoid overriding the same method in multiple derived interfaces. Doing so creates an ambiguous method call whenever a class implements both derived interfaces. The compiler can't pick a single better method so it issues an error. For example, if both the `IBlinkingLight` and `ITimerLight` implemented an override of `PowerStatus`, the `OverheadLight` would need to provide a more specific override. Otherwise, the compiler can't pick between the implementations in the two derived interfaces. You can usually avoid this situation by keeping interface definitions small and focused on one feature. In this scenario, each capability of a light is its own interface; only classes inherit multiple interfaces. +Avoid overriding the same method in multiple derived interfaces. Doing so creates an ambiguous method call whenever a class implements both derived interfaces. The compiler can't pick a single better method so it issues an error. For example, if both the `IBlinkingLight` and `ITimerLight` implemented an override of `PowerStatus`, the `OverheadLight` would need to provide a more specific override. Otherwise, the compiler can't pick between the implementations in the two derived interfaces. This situation is shown in the following diagram: -This sample shows one scenario where you can define discrete features that can be mixed into classes. You declare any set of supported functionality by declaring which interfaces a class supports. The use of virtual default interface methods enables classes to use or define a different implementation for any or all the interface methods. This language capability provides new ways to model the real-world systems you're building. Default interface methods provide a clearer way to express related classes that may mix and match different features using virtual implementations of those capabilities. +:::image type="content" source="./media/mixins-with-default-interface-methods/diamond-problem.svg" alt-text="illustration of the diamond problem with default interface methods"::: + +The preceding diagram illustrates the ambiguity. `OverheadLight` doesn't provide an implementation of `ILight.PowerStatus`. Both `IBlinkingLight` and `ITimerLight` provide overrides that are more specific. A call to `ILight.PowerStatus` on an instance of `OverheadLight` is ambiguous. You must add a new override in `OverheadLight` to resolve the ambiguity. + +You can usually avoid this situation by keeping interface definitions small and focused on one feature. In this scenario, each capability of a light is its own interface; only classes inherit multiple interfaces. + +This sample shows one scenario where you can define discrete features that can be mixed into classes. You declare any set of supported functionality by declaring which interfaces a class supports. The use of virtual default interface methods enables classes to use or define a different implementation for any or all the interface methods. This language capability provides new ways to model the real-world systems you're building. Default interface methods provide a clearer way to express related classes that might mix and match different features using virtual implementations of those capabilities. diff --git a/docs/csharp/language-reference/operators/member-access-operators.md b/docs/csharp/language-reference/operators/member-access-operators.md index 119e85721820f..c12287ffc3acc 100644 --- a/docs/csharp/language-reference/operators/member-access-operators.md +++ b/docs/csharp/language-reference/operators/member-access-operators.md @@ -1,7 +1,7 @@ --- title: "Member access and null-conditional operators and expressions:" description: "C# operators that you use to access type members or null-conditionally access type members. These operators include the dot operator - `.`, indexers - `[`, `]`, `^` and `..`, and invocation - `(`, `)`." -ms.date: 03/07/2024 +ms.date: 07/31/2024 author: pkulikov f1_keywords: - "._CSharpKeyword" @@ -58,7 +58,7 @@ You use the `.` token to access a member of a namespace or a type, as the follow Use a [`using` directive](../keywords/using-directive.md) to make the use of qualified names optional. -- Use `.` to access [type members](../../fundamentals/object-oriented/index.md#members), static and non-static, as the following code shows: +- Use `.` to access [type members](../../fundamentals/object-oriented/index.md#members), static and nonstatic, as the following code shows: :::code language="csharp" source="snippets/shared/MemberAccessOperators.cs" id="TypeMemberAccess" interactive="try-dotnet-method"::: @@ -66,7 +66,7 @@ You can also use `.` to access an [extension method](../../programming-guide/cla ## Indexer operator [] -Square brackets, `[]`, are typically used for array, indexer, or pointer element access. +Square brackets, `[]`, are typically used for array, indexer, or pointer element access. Beginning with C# 12, `[]` encloses a [collection expression](./collection-expressions.md). ### Array access @@ -92,7 +92,7 @@ For more information about indexers, see [Indexers](../../programming-guide/inde ### Other usages of [] -For information about pointer element access, see the [Pointer element access operator []](pointer-related-operators.md#pointer-element-access-operator-) section of the [Pointer related operators](pointer-related-operators.md) article. +For information about pointer element access, see the [Pointer element access operator []](pointer-related-operators.md#pointer-element-access-operator-) section of the [Pointer related operators](pointer-related-operators.md) article. For information about collection expressions, see the [collection expressions](./collection-expressions.md) article. You also use square brackets to specify [attributes](/dotnet/csharp/advanced-topics/reflection-and-attributes): @@ -103,7 +103,7 @@ void TraceMethod() {} ## Null-conditional operators `?.` and `?[]` -A null-conditional operator applies a [member access](#member-access-expression-) (`?.`) or [element access](#indexer-operator-) (`?[]`) operation to its operand only if that operand evaluates to non-null; otherwise, it returns `null`. That is: +A null-conditional operator applies a [member access](#member-access-expression-) (`?.`) or [element access](#indexer-operator-) (`?[]`) operation to its operand only if that operand evaluates to non-null; otherwise, it returns `null`. In other words: - If `a` evaluates to `null`, the result of `a?.x` or `a?[x]` is `null`. - If `a` evaluates to non-null, the result of `a?.x` or `a?[x]` is the same as the result of `a.x` or `a[x]`, respectively. diff --git a/docs/csharp/language-reference/tokens/interpolated.md b/docs/csharp/language-reference/tokens/interpolated.md index 5d777dd8fff87..2c90e78b89831 100644 --- a/docs/csharp/language-reference/tokens/interpolated.md +++ b/docs/csharp/language-reference/tokens/interpolated.md @@ -1,7 +1,7 @@ --- title: "$ - string interpolation - format string output" description: String interpolation using the `$` token provides a more readable and convenient syntax to format string output than traditional string composite formatting. -ms.date: 08/28/2023 +ms.date: 07/31/2024 f1_keywords: - "$_CSharpKeyword" - "$" @@ -14,7 +14,7 @@ author: pkulikov # String interpolation using `$` -The `$` character identifies a string literal as an _interpolated string_. An interpolated string is a string literal that might contain _interpolation expressions_. When an interpolated string is resolved to a result string, items with interpolation expressions are replaced by the string representations of the expression results. +The `$` character identifies a string literal as an _interpolated string_. An interpolated string is a string literal that might contain _interpolation expressions_. When an interpolated string is resolved to a result string, the compiler replaces items with interpolation expressions by the string representations of the expression results. String interpolation provides a more readable, convenient syntax to format strings. It's easier to read than [string composite formatting](../../../standard/base-types/composite-formatting.md). The following example uses both features to produce the same output: @@ -37,10 +37,10 @@ Elements in square brackets are optional. The following table describes each ele | Element | Description | |--|--| | `interpolationExpression` | The expression that produces a result to be formatted. When the expression is `null`, the output is the empty string (). | -| `alignment` | The constant expression whose value defines the minimum number of characters in the string representation of the expression result. If positive, the string representation is right-aligned; if negative, it's left-aligned. For more information, see the [Alignment component](../../../standard/base-types/composite-formatting.md#alignment-component) section of the [Composite formatting](../../../standard/base-types/composite-formatting.md) article. | -| `formatString` | A format string that is supported by the type of the expression result. For more information, see the [Format string component](../../../standard/base-types/composite-formatting.md#format-string-component) section of the [Composite formatting](../../../standard/base-types/composite-formatting.md) article. | +| `alignment` | The constant expression whose value defines the minimum number of characters in the string representation of the expression result. If positive, the string representation is right-aligned; if negative, left-aligned. For more information, see the [Alignment component](../../../standard/base-types/composite-formatting.md#alignment-component) section of the [Composite formatting](../../../standard/base-types/composite-formatting.md) article. | +| `formatString` | A format string supported by the type of the expression result. For more information, see the [Format string component](../../../standard/base-types/composite-formatting.md#format-string-component) section of the [Composite formatting](../../../standard/base-types/composite-formatting.md) article. | -The following example uses optional formatting components described above: +The following example uses optional formatting components described in the preceding table: :::code language="csharp" interactive="try-dotnet-method" source="./snippets/string-interpolation.cs" id="AlignAndSpecifyFormat"::: @@ -58,19 +58,19 @@ To embed `{` and `}` characters in the result string, start an interpolated raw :::code language="csharp" source="./snippets/string-interpolation.cs" id="InterpolatedRawStringLiteralWithBraces"::: -In the preceding example, an interpolated raw string literal starts with two `$` characters. That's why you need to put every interpolation expression between double braces, `{{` and `}}`. A single brace is embedded into a result string. If you need to embed repeated `{` or `}` characters into a result string, use an appropriately greater number of `$` characters to designate an interpolated raw string literal. +In the preceding example, an interpolated raw string literal starts with two `$` characters. You need to put every interpolation expression between double braces (`{{` and `}}`). A single brace is embedded into a result string. If you need to embed repeated `{` or `}` characters into a result string, use an appropriately greater number of `$` characters to designate an interpolated raw string literal. If the string literal has more repeated braces than the number of `$` characters, the `{` and `}` characters are grouped from inside to outside. In the preceding example, the literal `The point {{{X}}, {{Y}}}` interprets `{{X}}` and `{{Y}}` as interpolated expressions. The outer `{` and `}` are included verbatim in the output string. ## Special characters To include a brace, "{" or "}", in the text produced by an interpolated string, use two braces, "{{" or "}}". For more information, see the [Escaping braces](../../../standard/base-types/composite-formatting.md#escaping-braces) section of the [Composite formatting](../../../standard/base-types/composite-formatting.md) article. -As the colon (":") has special meaning in an interpolation expression item, to use a [conditional operator](../operators/conditional-operator.md) in an interpolation expression, enclose that expression in parentheses. +As the colon (":") has special meaning in an interpolation expression item, to use a [conditional operator](../operators/conditional-operator.md) in an interpolation expression. Enclose that expression in parentheses. The following example shows how to include a brace in a result string. It also shows how to use a conditional operator: :::code language="csharp" interactive="try-dotnet-method" source="./snippets/string-interpolation.cs" id="BraceAndConditional"::: -An interpolated [verbatim](verbatim.md) string starts with the both `$` and `@` characters. You can use `$` and `@` in any order: both `$@"..."` and `@$"..."` are valid interpolated verbatim strings. For more information about verbatim strings, see the [string](../builtin-types/reference-types.md) and [verbatim identifier](verbatim.md) articles. +An interpolated [verbatim](verbatim.md) string starts with both the `$` and `@` characters. You can use `$` and `@` in any order: both `$@"..."` and `@$"..."` are valid interpolated verbatim strings. For more information about verbatim strings, see the [string](../builtin-types/reference-types.md) and [verbatim identifier](verbatim.md) articles. ## Culture-specific formatting @@ -97,7 +97,7 @@ Beginning with C# 10 and .NET 6, the compiler checks if an interpolated string i > [!NOTE] > One side effect of interpolated string handlers is that a custom handler, including , may not evaluate all the interpolation expressions within the interpolated string under all conditions. That means side effects of those expressions may not occur. -Prior to C# 10, if an interpolated string has the type `string`, it's typically transformed into a method call. The compiler may replace with if the analyzed behavior would be equivalent to concatenation. +Before C# 10, if an interpolated string has the type `string`, it's typically transformed into a method call. The compiler can replace with if the analyzed behavior would be equivalent to concatenation. If an interpolated string has the type or , the compiler generates a call to the method. diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/static-constructors/Program.cs b/docs/csharp/programming-guide/classes-and-structs/snippets/static-constructors/Program.cs index 00f0b022b76c0..b08f81b94da6a 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/static-constructors/Program.cs +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/static-constructors/Program.cs @@ -88,3 +88,24 @@ static void Main() 72 is starting its route 31.00 minutes after global start time 3:57 PM. */ // + +// +public class Singleton +{ + // Static field initializer calls instance constructor. + private static Singleton instance = new Singleton(); + + private Singleton() + { + Console.WriteLine("Executes before static constructor."); + } + + static Singleton() + { + Console.WriteLine("Executes after instance constructor."); + } + + public static Singleton Instance => instance; +} +// + diff --git a/docs/csharp/programming-guide/classes-and-structs/static-constructors.md b/docs/csharp/programming-guide/classes-and-structs/static-constructors.md index adde11fb6f75e..c83b7a0219b43 100644 --- a/docs/csharp/programming-guide/classes-and-structs/static-constructors.md +++ b/docs/csharp/programming-guide/classes-and-structs/static-constructors.md @@ -1,24 +1,28 @@ --- title: "Static Constructors" description: A static constructor in C# initializes static data or performs an action done only once. It runs before the first instance is created or static members are referenced. -ms.date: 10/24/2023 +ms.date: 07/31/2024 helpviewer_keywords: - "static constructors [C#]" - "constructors [C#], static" --- # Static Constructors (C# Programming Guide) -A static constructor is used to initialize any [static](../../language-reference/keywords/static.md) data, or to perform a particular action that needs to be performed only once. It is called automatically before the first instance is created or any static members are referenced. A static constructor will be called at most once. +A static constructor is used to initialize any [static](../../language-reference/keywords/static.md) data, or to perform a particular action that needs to be performed only once. It's called automatically before the first instance is created or any static members are referenced. A static constructor is called at most once. -[!code-csharp[SimpleClass#1](snippets/static-constructors/Program.cs#1)] +:::code language="csharp" source="snippets/static-constructors/Program.cs" id="Snippet1"::: There are several actions that are part of static initialization. Those actions take place in the following order: -1. *Static fields are set to 0*. This is typically done by the runtime. +1. *Static fields are set to 0*. The runtime typically does this initialization. 1. *Static field initializers run*. The static field initializers in the most derived type run. 1. *Base type static field initializers run*. Static field initializers starting with the direct base through each base type to . -1. *Base static constructors run*. Any static constructors, starting with through each base class to the direct base class. -1. *The static constructor runs*. The static constructor for the type runs. +1. *Any static constructor runs*. Any static constructors, from the ultimate base class of through each base class through the type run. The order of static constructor execution isn't specified. However, all static constructors in the hierarchy run before any instances are created. + +> [!IMPORTANT] +> There is one important exception to the rule that a static constructor runs before any instance is created. If a static field initializer creates an instance of the type, that initializer runs (including any call to an instance constructor) before the static constructor runs. This is most common in the *singleton pattern* as shown in the following example: +> +> :::code language="csharp" source="./snippets/static-constructors/Program.cs" id="Singleton"::: A [module initializer](../../language-reference/attributes/general.md#moduleinitializer-attribute) can be an alternative to a static constructor. For more information, see the [specification for module initializers](~/_csharplang/proposals/csharp-9.0/module-initializers.md). @@ -28,30 +32,30 @@ Static constructors have the following properties: - A static constructor doesn't take access modifiers or have parameters. - A class or struct can only have one static constructor. -- Static constructors cannot be inherited or overloaded. -- A static constructor cannot be called directly and is only meant to be called by the common language runtime (CLR). It is invoked automatically. +- Static constructors can't be inherited or overloaded. +- A static constructor can't be called directly and is only meant to be called by the common language runtime (CLR). It's invoked automatically. - The user has no control on when the static constructor is executed in the program. -- A static constructor is called automatically. It initializes the [class](../../language-reference/keywords/class.md) before the first instance is created or any static members declared in that class (not its base classes) are referenced. A static constructor runs before an instance constructor. If static field variable initializers are present in the class of the static constructor, they're executed in the textual order in which they appear in the class declaration. The initializers run immediately prior to the execution of the static constructor. +- A static constructor is called automatically. It initializes the [class](../../language-reference/keywords/class.md) before the first instance is created or any static members declared in that class (not its base classes) are referenced. A static constructor runs before an instance constructor. If static field variable initializers are present in the class of the static constructor, they run in the textual order in which they appear in the class declaration. The initializers run immediately before the static constructor. - If you don't provide a static constructor to initialize static fields, all static fields are initialized to their default value as listed in [Default values of C# types](../../language-reference/builtin-types/default-values.md). -- If a static constructor throws an exception, the runtime doesn't invoke it a second time, and the type will remain uninitialized for the lifetime of the application domain. Most commonly, a exception is thrown when a static constructor is unable to instantiate a type or for an unhandled exception occurring within a static constructor. For static constructors that aren't explicitly defined in source code, troubleshooting may require inspection of the intermediate language (IL) code. +- If a static constructor throws an exception, the runtime doesn't invoke it a second time, and the type remains uninitialized for the lifetime of the application domain. Most commonly, a exception is thrown when a static constructor is unable to instantiate a type or for an unhandled exception occurring within a static constructor. For static constructors that aren't explicitly defined in source code, troubleshooting might require inspection of the intermediate language (IL) code. - The presence of a static constructor prevents the addition of the type attribute. This limits runtime optimization. -- A field declared as `static readonly` may only be assigned as part of its declaration or in a static constructor. When an explicit static constructor isn't required, initialize static fields at declaration rather than through a static constructor for better runtime optimization. -- The runtime calls a static constructor no more than once in a single application domain. That call is made in a locked region based on the specific type of the class. No additional locking mechanisms are needed in the body of a static constructor. To avoid the risk of deadlocks, don't block the current thread in static constructors and initializers. For example, don't wait on tasks, threads, wait handles or events, don't acquire locks, and don't execute blocking parallel operations such as parallel loops, `Parallel.Invoke` and Parallel LINQ queries. +- A field declared as `static readonly` can only be assigned as part of its declaration or in a static constructor. When an explicit static constructor isn't required, initialize static fields at declaration rather than through a static constructor for better runtime optimization. +- The runtime calls a static constructor no more than once in a single application domain. That call is made in a locked region based on the specific type of the class. No extra locking mechanisms are needed in the body of a static constructor. To avoid the risk of deadlocks, don't block the current thread in static constructors and initializers. For example, don't wait on tasks, threads, wait handles or events, don't acquire locks, and don't execute blocking parallel operations such as parallel loops, `Parallel.Invoke` and Parallel LINQ queries. -> [!Note] +> [!NOTE] > Though not directly accessible, the presence of an explicit static constructor should be documented to assist with troubleshooting initialization exceptions. ### Usage - A typical use of static constructors is when the class is using a log file and the constructor is used to write entries to this file. - Static constructors are also useful when creating wrapper classes for unmanaged code, when the constructor can call the `LoadLibrary` method. -- Static constructors are also a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile time via type-parameter constraints. +- Static constructors are also a convenient place to enforce run-time checks on the type parameter that can't be checked at compile time via type-parameter constraints. ## Example In this example, class `Bus` has a static constructor. When the first instance of `Bus` is created (`bus1`), the static constructor is invoked to initialize the class. The sample output verifies that the static constructor runs only one time, even though two instances of `Bus` are created, and that it runs before the instance constructor runs. -[!code-csharp[BusSample#2](snippets/static-constructors/Program.cs#2)] +:::code language="csharp" source="snippets/static-constructors/Program.cs" id="Snippet2"::: ## C# language specification diff --git a/docs/csharp/programming-guide/strings/index.md b/docs/csharp/programming-guide/strings/index.md index f4739a94538e4..9d82e74d3f45c 100644 --- a/docs/csharp/programming-guide/strings/index.md +++ b/docs/csharp/programming-guide/strings/index.md @@ -1,7 +1,7 @@ --- title: "Strings" description: Learn about strings in C# programming. See information on declaring and initializing strings, the immutability of string objects, and string escape sequences. -ms.date: 03/15/2024 +ms.date: 07/31/2024 helpviewer_keywords: - "C# language, strings" - "strings [C#]" @@ -13,7 +13,7 @@ A string is an object of type whose value is text. Internal ## string vs. System.String -In C#, the `string` keyword is an alias for ; therefore, `String` and `string` are equivalent. It's recommended to use the provided alias `string` as it works even without `using System;`. The `String` class provides many methods for safely creating, manipulating, and comparing strings. In addition, the C# language overloads some operators to simplify common string operations. For more information about the keyword, see [string](../../language-reference/builtin-types/reference-types.md). For more information about the type and its methods, see . +In C#, the `string` keyword is an alias for ; therefore, `String` and `string` are equivalent. Use the provided alias `string` as it works even without `using System;`. The `String` class provides many methods for safely creating, manipulating, and comparing strings. In addition, the C# language overloads some operators to simplify common string operations. For more information about the keyword, see [string](../../language-reference/builtin-types/reference-types.md). For more information about the type and its methods, see . ## Declaring and initializing strings @@ -23,7 +23,7 @@ You can declare and initialize strings in various ways, as shown in the followin You don't use the [new](../../language-reference/operators/new-operator.md) operator to create a string object except when initializing the string with an array of chars. -Initialize a string with the constant value to create a new object whose string is of zero length. The string literal representation of a zero-length string is "". By initializing strings with the value instead of [null](../../language-reference/keywords/null.md), you can reduce the chances of a occurring. Use the static method to verify the value of a string before you try to access it. +Initialize a string with the constant value to create a new object whose string is of zero length. The string literal representation of a zero-length string is `""`. By initializing strings with the value instead of [null](../../language-reference/keywords/null.md), you can reduce the chances of a occurring. Use the static method to verify the value of a string before you try to access it. ## Immutability of strings @@ -53,7 +53,7 @@ For more information about how to create new strings that are based on modificat Beginning with C# 11, you can use *raw string literals* to more easily create strings that are multi-line, or use any characters requiring escape sequences. *Raw string literals* remove the need to ever use escape sequences. You can write the string, including whitespace formatting, how you want it to appear in output. A *raw string literal*: -- Starts and ends with a sequence of at least three double quote characters (`"""`). You're allowed more than three consecutive characters to start and end the sequence in order to support string literals that contain three (or more) repeated quote characters. +- Starts and ends with a sequence of at least three double quote characters (`"""`). You can use more than three consecutive characters to start and end the sequence to support string literals that contain three (or more) repeated quote characters. - Single line raw string literals require the opening and closing quote characters on the same line. - Multi-line raw string literals require both opening and closing quote characters on their own line. - In multi-line raw string literals, any whitespace to the left of the closing quotes is removed from all lines of the raw string literal. @@ -70,12 +70,10 @@ The following examples demonstrate the compiler errors reported based on these r The first two examples are invalid because multiline raw string literals require the opening and closing quote sequence on its own line. The third example is invalid because the text is outdented from the closing quote sequence. -You should consider raw string literals when you're generating text that includes characters that require [escape sequences](#string-escape-sequences) when using quoted string literals or verbatim string literals. Raw string literals are easier for you and others to read because it will more closely resemble the output text. For example, consider the following code that includes a string of formatted JSON: +You should consider raw string literals when you're generating text that includes characters that require [escape sequences](#string-escape-sequences) when using quoted string literals or verbatim string literals. Raw string literals are easier for you and others to read because it more closely resembles the output text. For example, consider the following code that includes a string of formatted JSON: :::code language="csharp" source="./snippets/StringLiterals.cs" id="JSONString"::: -Compare that text with the equivalent text in the sample on [JSON deserialization](../../../standard/serialization/system-text-json/deserialization.md), which doesn't make use of this new feature. - ### String escape sequences | Escape sequence | Character name | Unicode encoding | @@ -108,7 +106,7 @@ A format string is a string whose contents are determined dynamically at run tim ### String interpolation -[*Interpolated strings*](../../language-reference/tokens/interpolated.md) are identified by the `$` special character and include interpolated expressions in braces. If you're new to string interpolation, see the [String interpolation - C# interactive tutorial](../../tutorials/exploration/interpolated-strings.yml) for a quick overview. +You declare [*Interpolated strings*](../../language-reference/tokens/interpolated.md) with the `$` special character. An interpolated string includes interpolated expressions in braces. If you're new to string interpolation, see the [String interpolation - C# interactive tutorial](../../tutorials/exploration/interpolated-strings.yml) for a quick overview. Use string interpolation to improve the readability and maintainability of your code. String interpolation achieves the same results as the `String.Format` method, but improves ease of use and inline clarity. @@ -130,7 +128,7 @@ To interpret escape sequences literally, use a [verbatim](../../language-referen ### Composite formatting -The utilizes placeholders in braces to create a format string. This example results in similar output to the string interpolation method used above. +The utilizes placeholders in braces to create a format string. This example results in similar output to the string interpolation method used in the preceding sample. :::code language="csharp" source="./snippets/StringInterpolation.cs" id="StringFormat":::