diff --git a/.openpublishing.redirection.csharp.json b/.openpublishing.redirection.csharp.json index eb3f47090da3a..b03dbc61de154 100644 --- a/.openpublishing.redirection.csharp.json +++ b/.openpublishing.redirection.csharp.json @@ -4174,7 +4174,7 @@ }, { "source_path_from_root": "/docs/csharp/tutorials/exploration/patterns-objects.md", - "redirect_url": "/dotnet/csharp/whats-new/tutorials/patterns-objects" + "redirect_url": "/dotnet/csharp/tutorials/patterns-objects" }, { "source_path_from_root": "/docs/csharp/tutorials/exploration/records.md", @@ -4297,13 +4297,17 @@ "source_path_from_root": "/docs/csharp/whats-new/csharp-7.md", "redirect_url": "/dotnet/csharp/whats-new/csharp-version-history#c-version-70" }, + { + "source_path_from_root": "/docs/csharp/whats-new/csharp-8.md", + "redirect_url": "/dotnet/csharp/whats-new/csharp-version-history#c-version-80" + }, { "source_path_from_root": "/docs/csharp/whats-new/csharp-9.md", "redirect_url": "/dotnet/csharp/whats-new/csharp-version-history#c-version-9" }, { - "source_path_from_root": "/docs/csharp/whats-new/csharp-8.md", - "redirect_url": "/dotnet/csharp/whats-new/csharp-version-history#c-version-80" + "source_path_from_root": "/docs/csharp/whats-new/csharp-10.md", + "redirect_url": "/dotnet/csharp/whats-new/csharp-version-history#c-version-10" }, { "source_path_from_root": "/docs/csharp/whats-new/index.md", @@ -4334,6 +4338,14 @@ "source_path_from_root": "/docs/csharp/whats-new/tutorials/ranges-indexes.md", "redirect_url": "/dotnet/csharp/tutorials/ranges-indexes" }, + { + "source_path_from_root": "/docs/csharp/whats-new/tutorials/interpolated-string-handler.md", + "redirect_url": "/dotnet/csharp/advanced-topics/performance/interpolated-string-handler" + }, + { + "source_path_from_root": "/docs/csharp/whats-new/tutorials/patterns-objects.md", + "redirect_url": "/dotnet/csharp/tutorials/patterns-objects" + }, { "source_path_from_root": "/docs/csharp/write-safe-efficient-code.md", "redirect_url": "/dotnet/csharp/advanced-topics/performance" diff --git a/.openpublishing.redirection.standard.json b/.openpublishing.redirection.standard.json index f6de36a45fed4..25dc618ea2cfd 100644 --- a/.openpublishing.redirection.standard.json +++ b/.openpublishing.redirection.standard.json @@ -524,6 +524,10 @@ "source_path_from_root": "/docs/standard/serialization/how-to-determine-if-netstandard-object-is-serializable.md", "redirect_url": "/dotnet/standard/serialization/binaryformatter-security-guide" }, + { + "source_path_from_root": "/docs/standard/serialization/system-text-json/supported-collection-types.md", + "redirect_url": "/dotnet/standard/serialization/system-text-json/supported-types" + }, { "source_path_from_root": "/docs/standard/index.yml", "redirect_url": "/dotnet/fundamentals/" diff --git a/docs/core/compatibility/networking/9.0/query-redaction-events.md b/docs/core/compatibility/networking/9.0/query-redaction-events.md index 5abb2986b22ca..d598d699664e3 100644 --- a/docs/core/compatibility/networking/9.0/query-redaction-events.md +++ b/docs/core/compatibility/networking/9.0/query-redaction-events.md @@ -7,7 +7,7 @@ ai-usage: ai-assisted # URI query and fragment redaction in HttpClient EventSource events -In .NET 9, the default behavior of events emitted by and (`EventSource` name: `System.Net.Http`) has been modified to scrub the query and the fragment part of the Uri. This change enhances privacy by preventing the logging of potentially sensitive information contained in query strings while keeping the performance costs of the redaction minimal. If necessary, you can override this behavior. +In .NET 9, the default behavior of events emitted by and (`EventSource` name: `System.Net.Http`) has been modified to scrub the query and fragment part of the URI. This change enhances privacy by preventing the logging of potentially sensitive information contained in query strings while keeping the performance costs of the redaction minimal. If necessary, you can override this behavior. ## Version introduced @@ -19,7 +19,7 @@ Previously, events emitted by `HttpClient` and `SocketsHttpHandler` included que ## New behavior -With the change in [dotnet/runtime#104741](https://github.com/dotnet/runtime/pull/104741), the query and the fragment part by a `*` character in `HttpClient` and `SocketsHttpHandler` events, by default. This change affects specific events and parameters such as `pathAndQuery` in `RequestStart` and `redirectUri` in `Redirect`. +With the change in [dotnet/runtime#104741](https://github.com/dotnet/runtime/pull/104741), the query and fragment part are replaced by a `*` character in `HttpClient` and `SocketsHttpHandler` events, by default. This change affects specific events and parameters, such as `pathAndQuery` in `RequestStart` and `redirectUri` in `Redirect`. ## Type of breaking change @@ -27,7 +27,7 @@ This change is a [behavioral change](../../categories.md#behavioral-change). ## Reason for change -The primary reason for this change was to enhance privacy by reducing the risk of sensitive information being logged inadvertently. Query strings often contain sensitive data, and redacting them from logs by default helps protect this information. To keep the implementation simple and efficient, the fragment part is also being scrubbed together with the query. +The primary reason for this change was to enhance privacy by reducing the risk of sensitive information being logged inadvertently. Query strings often contain sensitive data, and redacting them from logs by default helps protect this information. To keep the implementation simple and efficient, the fragment part is also scrubbed. ## Recommended action diff --git a/docs/core/compatibility/networking/9.0/query-redaction-logs.md b/docs/core/compatibility/networking/9.0/query-redaction-logs.md index d39253fe151a0..f39e9484929e4 100644 --- a/docs/core/compatibility/networking/9.0/query-redaction-logs.md +++ b/docs/core/compatibility/networking/9.0/query-redaction-logs.md @@ -19,7 +19,7 @@ Previously, the default implementation of `IHttpClientFactory` logging included ## New behavior -The messages passed to now have the query and the fragment part replaced by a `*` character. +The messages passed to now have the query and fragment part replaced by a `*` character. ## Type of breaking change @@ -27,7 +27,7 @@ This change is a [behavioral change](../../categories.md#behavioral-change). ## Reason for change -The primary reason for this change is to enhance privacy by reducing the risk of sensitive information being logged inadvertently. Query strings often contain sensitive data and excluding them from logs by default helps protect this information. The fragment part is being scrubbed to keep the implementation simple and efficient. +The primary reason for this change is to enhance privacy by reducing the risk of sensitive information being logged inadvertently. Query strings often contain sensitive data and excluding them from logs by default helps protect this information. To keep the implementation simple and efficient, the fragment part is also scrubbed. ## Recommended action diff --git a/docs/core/diagnostics/debugger-extensions.md b/docs/core/diagnostics/debugger-extensions.md new file mode 100644 index 0000000000000..5c05635a2c3ae --- /dev/null +++ b/docs/core/diagnostics/debugger-extensions.md @@ -0,0 +1,242 @@ +--- +title: .NET debugger extensions +description: Learn about the debugger extensions for .NET, which provide information about the internal CLR environment. +ms.date: 09/11/2023 +ms.topic: reference +helpviewer_keywords: + - ".NET debugger extensions" + - "debugger extensions" + - "SOS.dll" +--- + +# .NET debugger extensions + +The .NET debugger extensions let you view information about code that's running inside the .NET runtime, both on live processes and dumps. The extensions are preinstalled with [Windbg](/windows-hardware/drivers/debugger/debugger-download-tools), and can be [downloaded](dotnet-debugger-extensions.md) for use with LLDB. You can use the .NET debugger extensions to: + +- Collect information about the managed heap. +- Look for heap corruptions. +- Display internal data types used by the runtime. +- View information about all managed code running inside the runtime. + +The .NET debugger extensions include commands from the existing [dotnet-sos](dotnet-sos.md) tool as well as new commands. A limited set of commands are also now supported for the Native AOT runtime. + +## Syntax + +Under windbg: `![command] [options]` + +Under LLDB: `sos [command] [options]` + +Many of the commands have aliases or shortcuts under LLDB: `clrstack [options]` + +## Commands + +The following table of commands is also available under **Help** or **soshelp**. Individual command help is available using `soshelp `. + +| Command | Description | +|---------|-------------| +| **bpmd** [**-nofuturemodule**] [\<*module name*> \<*method name*>] [**-md** <`MethodDesc`>] **-list** **-clear** \<*pending breakpoint number*> **-clearall** | Creates a breakpoint at the specified method in the specified module.

If the specified module and method has not been loaded, this command waits for a notification that the module was loaded and just-in-time (JIT) compiled before creating a breakpoint.

You can manage the list of pending breakpoints by using the **-list**, **-clear**, and **-clearall** options:

The **-list** option generates a list of all the pending breakpoints. If a pending breakpoint has a nonzero module ID, that breakpoint is specific to a function in that particular loaded module. If the pending breakpoint has a zero module ID, that breakpoint applies to modules that has not yet been loaded.

Use the **-clear** or **-clearall** option to remove pending breakpoints from the list. | +| **CLRStack** [**-a**] [**-l**] [**-p**] [**-n**] [**-f**] [**-r**] [**-all**] | Provides a stack trace of managed code only.

The **-p** option shows arguments to the managed function.

The **-l** option shows information on local variables in a frame. The .NET debugger extensions cannot retrieve local names, so the output for local names is in the format \<*local address*> **=** \<*value*>.

The **-a** option is a shortcut for **-l** and **-p** combined.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS looks up the symbols for every managed frame and if successful, displays the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

The **-f** option (full mode) displays the native frames intermixing them with the managed frames and the assembly name and function offset for the managed frames. This option does not display native frames when used with `dotnet-dump`.

The **-r** option dumps the registers for each stack frame.

The **-all** option dumps the stacks of all the managed threads. | +| **COMState** | Lists the COM apartment model for each thread and a `Context` pointer, if available. This command is only supported on Windows. | +| **DumpArray** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-details**] [**-nofields**] \<*array object address*>

-or-

**DA** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-details**] [**-nofields**] *array object address*> | Examines elements of an array object.

The **-start** option specifies the starting index at which to display elements.

The **-length** option specifies how many elements to show.

The **-details** option displays details of the element using the **DumpObj** and **DumpVC** formats.

The **-nofields** option prevents arrays from displaying. This option is available only when the **-details** option is specified. | +| **DumpAsync** (**dumpasync**) [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [**-waiting**] [**-roots**] | DumpAsync traverses the garbage-collected heap and looks for objects representing async state machines as created when an async method's state is transferred to the heap. This command recognizes async state machines defined as `async void`, `async Task`, `async Task`, `async ValueTask`, and `async ValueTask`.

The output includes a block of details for each async state machine object found. These details include:
- A line for the type of the async state machine object, including its MethodTable address, its object address, its size, and its type name.
- A line for the state machine type name as contained in the object.
- A listing of each field on the state machine.
- A line for a continuation from this state machine object, if one or more has been registered.
- Discovered GC roots for this async state machine object.

Supported for Native AOT applications. | +| **DumpAssembly** \<*assembly address*> | Displays information about an assembly.

The **DumpAssembly** command lists multiple modules, if they exist.

You can get an assembly address by using the **DumpDomain** command. | +| **DumpClass** \<*EEClass address*> | Displays information about the `EEClass` structure associated with a type.

The **DumpClass** command displays static field values but does not display nonstatic field values.

Use the **DumpMT**, **DumpObj**, **Name2EE**, or **Token2EE** command to get an `EEClass` structure address. | +| **DumpDomain** [\<*domain address*>] | Enumerates each object that's loaded within the specified object address. When called with no parameters, the **DumpDomain** command lists all objects in a process. Since .NET (Core) only has one , **DumpDomain** only returns one object. | +| **DumpHeap** [**-stat**] [**-strings**] [**-short**] [**-min** \<*size*>] [**-max** \<*size*>] [**-thinlock**] [**-startAtLowerBound**] [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [*start* [*end*]] | Displays information about the garbage-collected heap and collection statistics about objects.

The **DumpHeap** command displays a warning if it detects excessive fragmentation in the garbage collector heap.

The **-stat** option restricts the output to the statistical type summary.

The **-strings** option restricts the output to a statistical string value summary.

The **-short** option limits output to just the address of each object. This command lets you easily pipe output from the command to another debugger command for automation.

The **-min** option ignores objects that are less than the `size` parameter, specified in bytes.

The **-max** option ignores objects that are larger than the `size` parameter, specified in bytes.

The **-thinlock** option reports ThinLocks. For more information, see the **SyncBlk** command.

The `-startAtLowerBound` option forces the heap walk to begin at the lower bound of a supplied address range. During the planning phase, the heap is often not walkable because objects are being moved. This option forces **DumpHeap** to begin its walk at the specified lower bound. You must supply the address of a valid object as the lower bound for this option to work. You can display memory at the address of a bad object to manually find the next method table. If the garbage collection is currently in a call to `memcopy`, you may also be able to find the address of the next object by adding the size to the start address, which is supplied as a parameter.

The **-mt** option lists only those objects that correspond to the specified `MethodTable` structure.

The **-type** option lists only those objects whose type name is a substring match of the specified string.

The `start` parameter begins listing from the specified address.

The `end` parameter stops listing at the specified address.

Supported for Native AOT applications. | +| **DumpIL** \<*Managed DynamicMethod object*> | \<*DynamicMethodDesc pointer*> | \<*MethodDesc pointer*> | Displays the common intermediate language (CIL) that's associated with a managed method.

Dynamic CIL is emitted differently than CIL that's loaded from an assembly. Dynamic CIL refers to objects in a managed object array rather than to metadata tokens. | +| **DumpLock** [**--allthreads**] [**--waits**] | Displays information about System.Threading.Lock objects, such as those being held by threads (default), or those being waited upon by threads. | +| **DumpLog** [**-addr** \<*addressOfStressLog*>] [<*Filename*>] | Writes the contents of an in-memory stress log to the specified file. If you do not specify a name, this command creates a file called StressLog.txt in the current directory.

The in-memory stress log helps you diagnose stress failures without using locks or I/O. To enable the stress log, set the following registry keys under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\\.NETFramework:

(DWORD) StressLog = 1

(DWORD) LogFacility = 0xffffffff

(DWORD) StressLogSize = 65536

The optional `-addr` option lets you specify a stress log other than the default log.

Supported for Native AOT applications. | +| **DumpMD** \<*MethodDesc address*> | Displays information about a `MethodDesc` structure at the specified address.

You can use the **IP2MD** command to get the `MethodDesc` structure address from a managed function. | +| **DumpMT** [**-MD**] \<*MethodTable address*> | Displays information about a method table at the specified address. Specifying the **-MD** option displays a list of all methods defined with the object.

Each managed object contains a method table pointer. | +| **DumpModule** [**-mt**] \<*Module address*> | Displays information about a module at the specified address. The **-mt** option displays the types defined in a module and the types referenced by the module

You can use the **DumpDomain** or **DumpAssembly** command to retrieve a module's address. | +| **DumpObj** [**-nofields**] \<*object address*>

-or-

**DO** \<*object address*> | Displays information about an object at the specified address. The **DumpObj** command displays the fields, the `EEClass` structure information, the method table, and the size of the object.

You can use the **DumpStackObjects** command to retrieve an object's address.

You can run the **DumpObj** command on fields of type `CLASS` because they are also objects.

The `-`**nofields** option prevents fields of the object being displayed, it is useful for objects like String. | +| **DumpRuntimeTypes** | Displays the runtime type objects in the garbage collector heap and lists their associated type names and method tables. | +| **DumpStack** [**-EE**] [**-n**] [`top` *stack* [`bottom` *stack*]] | Displays a stack trace.

The **-EE** option causes the **DumpStack** command to display only managed functions. Use the `top` and `bottom` parameters to limit the stack frames displayed on x86 platforms.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful displays the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior. | +| **DumpSig** \<*sigaddr*> \<*moduleaddr*> | Displays information about a `Sig` structure at the specified address. | +| **DumpSigElem** \<*sigaddr*> \<*moduleaddr*> | Displays a single element of a signature object. In most cases, you should use **DumpSig** to look at individual signature objects. However, if a signature has been corrupted in some way, you can use **DumpSigElem** to read the valid portions of it. | +| **DumpStackObjects** [**-verify**] [`top` *stack* [`bottom` *stack*]]

-or-

**DSO** [**-verify**] [`top` *stack* [`bottom` *stack*]] | Displays all managed objects found within the bounds of the current stack.

The **-verify** option validates each nonstatic `CLASS` field of an object field.

Use the **DumpStackObject** command with stack tracing commands such as **K** (windbg) or **bt** (lldb) along with the **clrstack** command to determine the values of local variables and parameters. | +| **DumpVC** \<*MethodTable address*> \<*Address*> | Displays information about the fields of a value class at the specified address.

The **MethodTable** parameter allows the **DumpVC** command to correctly interpret fields. Value classes do not have a method table as their first field. | +| **EEHeap** [**-gc**] [**-loader**] | Displays information about process memory consumed by internal runtime data structures.

The **-gc** and **-loader** options limit the output of this command to garbage collector or loader data structures.

The information for the garbage collector lists the ranges of each segment in the managed heap. If the pointer falls within a segment range given by **-gc**, the pointer is an object pointer. | +| **EEStack** [**-short**] [**-EE**] | Runs the **DumpStack** command on all threads in the process.

The **-EE** option is passed directly to the **DumpStack** command. The **-short** parameter limits the output to the following kinds of threads:

Threads that have taken a lock.

Threads that have been stalled in order to allow a garbage collection.

Threads that are currently in managed code. | +| **EHInfo** [\<*MethodDesc address*>] [\<*Code address*>] | Displays the exception handling blocks in a specified method. This command displays the code addresses and offsets for the clause block (the `try` block) and the handler block (the `catch` block). | +| **FAQ** | Displays frequently asked questions. Not supported in `dotnet-dump`. | +| **FinalizeQueue** [**-detail**] | [**-allReady**] [**-short**] | Displays all objects registered for finalization.

The **-detail** option displays extra information about any `SyncBlocks` that need to be cleaned up, and any `RuntimeCallableWrappers` (RCWs) that await cleanup. Both of these data structures are cached and cleaned up by the finalizer thread when it runs.

The `-allReady` option displays all objects that are ready for finalization, regardless of whether they are already marked by the garbage collection as such, or will be marked by the next garbage collection. The objects that are in the "ready for finalization" list are finalizable objects that are no longer rooted. This option can be expensive, because it verifies whether all the objects in the finalizable queues are still rooted.

The `-short` option limits the output to the address of each object. If it is used in with **-allReady**, it enumerates all objects that have a finalizer that are no longer rooted. If it is used independently, it lists all objects in the finalizable and "ready for finalization" queues. | +| **FindAppDomain** \<*Object address*> | Determines the application domain of an object at the specified address. | +| **FindRoots** **-gen** \<*N*> | **-gen any** |\<*object address*> | Causes the debugger to break in the debuggee on the next collection of the specified generation. The effect is reset as soon as the break occurs. To break on the next collection, you have to reissue the command. The *\* form of this command is used after the break caused by the **-gen** or **-gen any** has occurred. At that time, the debuggee is in the right state for **FindRoots** to identify roots for objects from the current condemned generations. Only supported on Windows. | +| **GCHandles** [**-perdomain**] | Displays statistics about garbage collector handles in the process.

The **-perdomain** option arranges the statistics by application domain.

Use the **GCHandles** command to find memory leaks caused by garbage collector handle leaks. For example, a memory leak occurs when code retains a large array because a strong garbage collector handle still points to it, and the handle is discarded without freeing it.

Only supported on Windows. | +| **GCHandleLeaks** | Searches memory for any references to strong and pinned garbage collector handles in the process and displays the results. If a handle is found, the **GCHandleLeaks** command displays the address of the reference. If a handle is not found in memory, this command displays a notification. Only supported on Windows. | +| **GCInfo** \<*MethodDesc address*>\<*Code address*> | Displays data that indicates when registers or stack locations contain managed objects. If a garbage collection occurs, the collector must know the locations of references to objects so it can update them with new object pointer values. | +| **GCRoot** [**-nostacks**] [**-all**] \<*Object address*> | Displays information about references (or roots) to an object at the specified address.

The **GCRoot** command examines the entire managed heap and the handle table for handles within other objects and handles on the stack. Each stack is then searched for pointers to objects, and the finalizer queue is also searched.

This command does not determine whether a stack root is valid or is discarded. Use the **clrstack** and **U** commands to disassemble the frame that the local or argument value belongs to in order to determine if the stack root is still in use.

The **-nostacks** option restricts the search to garbage collector handles and reachable objects.

The **-all** option forces all roots to be displayed instead of just the unique roots. | +| **GCWhere** *\* | Displays the location and size in the garbage collection heap of the argument passed in. When the argument lies in the managed heap but is not a valid object address, the size is displayed as 0 (zero). | +| **Help** (**soshelp**) [\<*command*>] [`faq`] | Displays all available commands when no parameter is specified, or displays detailed help information about the specified command.

The `faq` parameter displays answers to frequently asked questions. | +| **HeapStat** [**-inclUnrooted** | **-iu**] | Displays the generation sizes for each heap and the total free space in each generation on each heap. If the -**inclUnrooted** option is specified, the report includes information about the managed objects from the garbage collection heap that is no longer rooted. Only supported on Windows. | +| **HistClear** | Releases any resources used by the family of `Hist` commands.

Generally, you do not have to explicitly call `HistClear`, because each `HistInit` cleans up the previous resources. | +| **HistInit** | Initializes the SOS structures from the stress log saved in the debuggee. | +| **HistObj** ** | Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument. | +| **HistObjFind** ** | Displays all the log entries that reference an object at the specified address. | +| **HistRoot** *\* | Displays information related to both promotions and relocations of the specified root.

The root value can be used to track the movement of an object through the garbage collections. | +| **IP2MD** (**ip2md**) \<*Code address*> | Displays the `MethodDesc` structure at the specified address in code that has been JIT-compiled. | +| **ListNearObj** (**lno**) ** | Displays the objects preceding and following the specified address. The command looks for the address in the garbage collection heap that looks like a valid beginning of a managed object (based on a valid method table) and the object following the argument address. Only supported on Windows. | +| **MinidumpMode** [**0**] [**1**] | Prevents running unsafe commands when using a minidump.

Pass **0** to disable this feature or **1** to enable this feature. By default, the **MinidumpMode** value is set to **0**.

Minidumps created with the **.dump /m** command or **.dump** command have limited CLR-specific data and allow you to run only a subset of SOS commands correctly. Some commands may fail with unexpected errors because required areas of memory are not mapped or are only partially mapped. This option protects you from running unsafe commands against minidumps.

Only supported with Windbg. | +| **Name2EE** (**name2ee**) \<*module name*> \<*type or method name*>

-or-

**Name2EE** \<*module name*>**!**\<*type or method name*> | Displays the `MethodTable` structure and `EEClass` structure for the specified type or method in the specified module.

The specified module must be loaded in the process.

To get the proper type name, browse the module by using the [Ildasm.exe (IL Disassembler)](../../../docs/framework/tools/ildasm-exe-il-disassembler.md). You can also pass `*` as the module name parameter to search all loaded managed modules. The *module name* parameter can also be the debugger's name for a module, such as `mscorlib` or `image00400000`.

This command supports the Windows debugger syntax of <`module`>`!`<`type`>. The type must be fully qualified. | +| **ObjSize** [\<*Object address*>] | [**-aggregate**] [**-stat**] | Displays the size of the specified object. If you do not specify any parameters, the **ObjSize** command displays the size of all objects found on managed threads, displays all garbage collector handles in the process, and totals the size of any objects pointed to by those handles. The **ObjSize** command includes the size of all child objects in addition to the parent.

The **-aggregate** option can be used in with the **-stat** argument to get a detailed view of the types that are still rooted. By using **!dumpheap -stat** and **!objsize -aggregate -stat**, you can determine which objects are no longer rooted and diagnose various memory issues.

Only supported on Windows. | +| **PrintException** [**-nested**] [**-lines**] [\<*Exception object address*>]

-or-

**PE** [**-nested**] [\<*Exception object address*>] | Displays and formats fields of any object derived from the class at the specified address. If you do not specify an address, the **PrintException** command displays the last exception thrown on the current thread.

The **-nested** option displays details about nested exception objects.

The **-lines** option displays source information, if available.

You can use this command to format and view the `_stackTrace` field, which is a binary array. | +| **ProcInfo** [**-env**] [**-time**] [**-mem**] | Displays environment variables for the process, kernel CPU time, and memory usage statistics. Only supported with Windbg. | +| **RCWCleanupList** \<*RCWCleanupList address*> | Displays the list of runtime callable wrappers at the specified address that are awaiting cleanup. Only supported with Windbg. | +| **SaveModule** \<*Base address*> \<*Filename*> | Writes an image, which is loaded in memory at the specified address, to the specified file. Only supported with Windbg. | +| **SetHostRuntime** [\] | This command sets the path to the .NET runtime to use to host the managed code that runs as part of SOS in the debugger (LLDB). The runtime needs to be at least version 2.1.0 or greater. If there are spaces in directory, it needs to be single-quoted (').

Normally, SOS attempts to find an installed .NET runtime to run its managed code automatically but this command is available if it fails. The default is to use the same runtime (libcoreclr) being debugged. Use this command if the default runtime being debugged isn't working enough to run the SOS code or if the version is less than 2.1.0.

If you received the following error message when running a SOS command, use this command to set the path to 2.1.0 or greater .NET runtime.

`(lldb) clrstack`
`Error: Fail to initialize CoreCLR 80004005 ClrStack failed`

`(lldb) sethostruntime /usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.6`

You can use the "dotnet --info" in a command shell to find the path of an installed .NET runtime. | +| **SetSymbolServer** [**-ms**] [**-disable**] [**-log**] [**-loadsymbols**] [**-cache** \] [**-directory** \] [**-sympath** \] [\] | Enables the symbol server downloading support.

The **-ms** option enables downloading from the public Microsoft symbol server.

The **-disable** option turns on the symbol download support.

The **-cache** \ option specifies a symbol cache directory. The default is $HOME/.dotnet/symbolcache if not specified.

The **-directory** option adds a path to search for symbols. Can be more than one.

The **-sympath** option adds server, cache, and directory paths in the Windows symbol path format.

The **-log** option enables symbol download logging.

The **-loadsymbols** option attempts to download the native .NET symbols for the runtime. Supported on lldb and dotnet-dump. | +| **SOSFlush** | Flushes an internal SOS cache. | +| **SOSStatus** [**-reset**] | Displays internal SOS status or reset the internal cached state. | +| **StopOnException** [**-derived**] [**-create** | **-create2**] \<*Exception*> \<*Pseudo-register number*> | Causes the debugger to stop when the specified exception is thrown, but to continue running when other exceptions are thrown.

The **-derived** option catches the specified exception and every exception that derives from the specified exception.

Only supported with Windbg. | +| **SyncBlk** [**-all** | \<*syncblk number*>] | Displays the specified `SyncBlock` structure or all `SyncBlock` structures. If you do not pass any arguments, the **SyncBlk** command displays the `SyncBlock` structure corresponding to objects that are owned by a thread.

A `SyncBlock` structure is a container for extra information that does not need to be created for every object. It can hold COM interop data, hash codes, and locking information for thread-safe operations. | +| **ThreadPool** | Displays information about the managed thread pool, including the number of work requests in the queue, the number of completion port threads, and the number of timers. | +| **Threads** (**clrthreads**) [**-live**] [**-special**] | Displays all managed threads in the process.

The **Threads** command displays the debugger shorthand ID, the CLR thread ID, and the operating system thread ID. Additionally, the **Threads** command displays a Domain column that indicates the application domain in which a thread is executing, an APT column that displays the COM apartment mode, and an Exception column that displays the last exception thrown in the thread.

The **-live** option displays threads associated with a live thread.

The **-special** option displays all special threads created by the CLR. Special threads include garbage collection threads (in concurrent and server garbage collection), debugger helper threads, finalizer threads, unload threads, and thread pool timer threads.

Supported for Native AOT applications. | +| **ThreadState \<** *State value field* **>** | Displays the state of the thread. The `value` parameter is the value of the `State` field in the **Threads** report output. | +| **Token2EE** \<*module name*> \<*token*> | Turns the specified metadata token in the specified module into a `MethodTable` structure or `MethodDesc` structure.

You can pass `*` for the module name parameter to find what that token maps to in every loaded managed module. You can also pass the debugger's name for a module, such as `mscorlib` or `image00400000`. | +| **U** [**-gcinfo**] [**-ehinfo**] [**-n**] \<*MethodDesc address*> | \<*Code address*> | Displays an annotated disassembly of a managed method specified either by a `MethodDesc` structure pointer for the method or by a code address within the method body. The **U** command displays the entire method from start to finish, with annotations that convert metadata tokens to names.

The **-gcinfo** option causes the **U** command to display the `GCInfo` structure for the method.

The **-ehinfo** option displays exception information for the method. You can also obtain this information with the **EHInfo** command.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS looks up the symbols for every managed frame and, if successful, displays the corresponding source file name and line number. You can specify the **-n** option to disable this behavior. | +| **VerifyHeap** | Checks the garbage collector heap for signs of corruption and displays any errors found.

Heap corruptions can be caused by platform invoke calls that are constructed incorrectly.

Supported for Native AOT applications. | +| **VerifyObj** \<*object address*> | Checks the object that is passed as an argument for signs of corruption. Only supported on Windows. | +| **VMMap** | Traverses the virtual address space and displays the type of protection applied to each region. Only supported with Windbg. | +| **VMStat** | Provides a summary view of the virtual address space, ordered by each type of protection applied to that memory (free, reserved, committed, private, mapped, image). The TOTAL column displays the result of the AVERAGE column multiplied by the BLK COUNT column. Only supported with Windbg. | + +### Windows Debugger + +You can also use the .NET debugger extensions by loading it into the [WinDbg/dbg debugger](/windows-hardware/drivers/debugger/debugger-download-tools) and executing commands within the Windows debugger. The commands can be used on live processes or dumps. + +Windbg should load the extension automatically whenever the process being debugged contains the .NET runtime (coreclr.dll or libcoreclr.so). + +### LLDB Debugger + +For instructions on configuring the .NET debugger extensions for LLDB, see [dotnet-debugger-extensions](dotnet-debugger-extensions.md). The commands can be used on live processes or dumps. + +By default you can reach all the commands by entering: `sos [command_name]`. However, the common commands have been aliased so that you don't need the `sos` prefix: + +| Command | Function | +| ------------------------------------- | ------------------------------------------------------------------------------------------- | +| `analyzeoom` | Displays the info of the last OOM that occurred on an allocation request to the GC heap. | +| `bpmd` | Creates a breakpoint at the specified managed method in the specified module. | +| `clrmodules` | Lists the managed modules in the process. | +| `clrstack` | Provides a stack trace of managed code only. | +| `clrthreads` | Lists the managed threads that are running. | +| `clru` | Displays an annotated disassembly of a managed method. | +| `dbgout` | Enables/disables (`-off`) internal SOS logging. | +| `dso` | Displays all managed objects found within the bounds of the current stack. | +| `dumpalc` | Displays details about a collectible AssemblyLoadContext to which the specified object is loaded. | +| `dumparray` | Displays details about a managed array. | +| `dumpasync` | Displays info about async state machines on the garbage-collected heap. | +| `dumpassembly` | Displays details about an assembly. | +| `dumpclass` | Displays information about the `EEClass` structure at the specified address. | +| `dumpconcurrentdictionary` | Displays concurrent dictionary content. | +| `dumpconcurrentqueue` | Displays concurrent queue content. | +| `dumpdelegate` | Displays information about a delegate. | +| `dumpdomain` | Displays information about the all assemblies within all the AppDomains or the specified one. | +| `dumpgcdata` | Displays information about the GC data. | +| `dumpgen` | Displays heap content for the specified generation. | +| `dumpheap` | Displays info about the garbage-collected heap and collection statistics about objects. | +| `dumpil` | Displays the common intermediate language (CIL) that's associated with a managed method. | +| `dumplock` | Displays information about System.Threading.Lock objects, such as those being held by threads (default), or those being waited upon by threads. This command is available in [.NET debugger extensions](dotnet-debugger-extensions.md) only. | +| `dumplog` | Writes the contents of an in-memory stress log to the specified file. | +| `dumpmd` | Displays information about the `MethodDesc` structure at the specified address. | +| `dumpmodule` | Displays information about the module at the specified address. | +| `dumpmt` | Displays information about the method table at the specified address. | +| `dumpobj` | Displays info the object at the specified address. | +| `dumpruntimetypes` | Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too. | +| `dumpsig` | Dumps the signature of a method or field specified by ` `. | +| `dumpsigelem` | Dumps a single element of a signature object. | +| `dumpstack` | Displays a native and managed stack trace. | +| `dumpstackobjects` | Displays all managed objects found within the bounds of the current stack. | +| `dumpvc` | Displays info about the fields of a value class. | +| `eeheap` | Displays info about process memory consumed by internal runtime data structures. | +| `eestack` | Runs `dumpstack` on all threads in the process. | +| `eeversion` | Displays information about the runtime and SOS versions. | +| `ehinfo` | Displays the exception handling blocks in a JIT-ed method. | +| `finalizequeue` | Displays all objects registered for finalization. | +| `findappdomain` | Attempts to resolve the AppDomain of a GC object. | +| `findroots` | Finds and displays object roots across GC collections. | +| `gchandles` | Displays statistics about garbage collector handles in the process. | +| `gcheapstat` | Displays statistics about garbage collector. | +| `gcinfo` | Displays the JIT GC encoding for a method. | +| `gcroot` | Displays info about references (or roots) to the object at the specified address. | +| `gcwhere` | Displays the location in the GC heap of the specified address. | +| `histclear` | Releases any resources used by the family of Hist commands. | +| `histinit` | Initializes the SOS structures from the stress log saved in the debuggee. | +| `histobj` | Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument. | +| `histobjfind` | Displays all the log entries that reference the object at the specified address. | +| `histroot` | Displays information related to both promotions and relocations of the specified root. | +| `histstats` | Displays stress log stats. | +| `ip2md` | Displays the `MethodDesc` structure at the specified address in code that has been JIT-compiled. | +| `listnearobj` | Displays the object preceding and succeeding the specified address. | +| `loadsymbols` | Loads the .NET native module symbols. | +| `logging` | Enables/disables internal SOS logging. | +| `name2ee` | Displays the `MethodTable` and `EEClass` structures for the specified type or method in the specified module. | +| `objsize` | Displays the size of the specified object. | +| `parallelstacks` | Displays the merged threads stack similarly to the Visual Studio 'Parallel Stacks' panel. | +| `pathto` | Displays the GC path from `` to ``. | +| `pe` | Displays and formats fields of any object derived from the class at the specified address. | +| `printexception` | Displays and formats fields of any object derived from the class at the specified address. | +| `runtimes` | Lists the runtimes in the target or change the default runtime. | +| `stoponcatch` | Target process will break the next time a managed exception is caught during execution. | +| `setclrpath` | Sets the path to load coreclr dac/dbi files. `setclrpath `. | +| `sethostruntime` | Sets or displays the .NET runtime directory to use to run managed code in SOS. | +| `setsymbolserver` | Enables the symbol server support. | +| `setsostid` | Sets the current OS tid/thread index instead of using the one LLDB provides. `setsostid `. | +| `sos` | Executes various coreclr debugging commands. Use the syntax `sos `. For more information, see 'soshelp'. | +| `soshelp` | Displays all available commands when no parameter is specified, or displays detailed help information about the specified command: `soshelp `. | +| `syncblk` | Displays the SyncBlock holder info. | +| `taskstate` | Displays a Task state in a human readable format. | +| `threadpool` | Displays info about the runtime thread pool. | +| `threadpoolqueue` | Displays queued thread pool work items. | +| `threadstate` | Pretty prints the meaning of a threads state. | +| `timerinfo` | Displays information about running timers. | +| `token2ee` | Displays the MethodTable structure and MethodDesc structure for the specified token and module. | +| `traverseheap` | Writes out heap information to a file in a format understood by the CLR Profiler. | +| `verifyheap` | Checks the GC heap for signs of corruption. | +| `verifyobj` | Checks the object that is passed as an argument for signs of corruption. | + +## Windbg/cdb example usage + +| Command | Description | +| - | - | +| `!dumparray -start 2 -length 5 -details 00ad28d0` | Displays the contents of an array at the address `00ad28d0`. The display starts from the second element and continues for five elements. | +| `!dumpassembly 1ca248` | Displays the contents of an assembly at the address `1ca248`. | +| `!dumpheap` | Displays information about the garbage collector heap. | +| `!DumpLog` | Writes the contents of the in-memory stress log to a (default) file called StressLog.txt in the current directory. | +| `!dumpmd 902f40` | Displays the `MethodDesc` structure at the address `902f40`. | +| `!dumpmodule 1caa50` | Displays information about a module at the address `1caa50`. | +| `!DumpObj a79d40` | Displays information about an object at the address `a79d40`. | +| `!DumpVC 0090320c 00a79d9c` | Displays the fields of a value class at the address `00a79d9c` using the method table at the address `0090320c`. | +| `!eeheap` -gc | Displays the process memory used by the garbage collector. | +| `!finalizequeue` | Displays all objects scheduled for finalization. | +| `!findappdomain 00a79d98` | Determines the application domain of an object at the address `00a79d98`. | +| `!gcinfo 5b68dbb8` | Displays all garbage collector handles in the current process. | +| `!name2ee unittest.exe MainClass.Main` | Displays the `MethodTable` and `EEClass` structures for the `Main` method in the class `MainClass` in the module `unittest.exe`. | +| `!token2ee unittest.exe 02000003` | Displays information about the metadata token at the address `02000003` in the module `unittest.exe`. | + +## LLDB example usage + +| Command | Description | +| - | - | +| `dumparray -start 2 -length 5 -details 00ad28d0` | Displays the contents of an array at the address `00ad28d0`. The display starts from the second element and continues for five elements. | +| `dumpassembly 1ca248` | Displays the contents of an assembly at the address `1ca248`. | +| `dumpheap` | Displays information about the garbage collector heap. | +| `dumplog` | Writes the contents of the in-memory stress log to a (default) file called StressLog.txt in the current directory. | +| `dumpmd 902f40` | Displays the `MethodDesc` structure at the address `902f40`. | +| `dumpmodule 1caa50` | Displays information about a module at the address `1caa50`. | +| `dumpobj a79d40` | Displays information about an object at the address `a79d40`. | +| `dumpvc 0090320c 00a79d9c` | Displays the fields of a value class at the address `00a79d9c` using the method table at the address `0090320c`. | +| `eeheap -gc` | Displays the process memory used by the garbage collector. | +| `findappdomain 00a79d98` | Determines the application domain of an object at the address `00a79d98`. | +| `gcinfo 5b68dbb8` | Displays all garbage collector handles in the current process. | +| `name2ee unittest.exe MainClass.Main` | Displays the `MethodTable` and `EEClass` structures for the `Main` method in the class `MainClass` in the module `unittest.exe`. | +| `token2ee unittest.exe 02000003` | Displays information about the metadata token at the address `02000003` in the module `unittest.exe`. | +| `clrthreads` | Displays the managed threads. | + +## See also + +- [An introduction to dumps in .NET](dumps.md) +- [Learn how to debug a memory leak in .NET](debug-memory-leak.md) +- [Collecting and analyzing memory dumps blog](https://devblogs.microsoft.com/dotnet/collecting-and-analyzing-memory-dumps/) +- [Dump analysis tool (dotnet-dump)](dotnet-dump.md) +- [.NET debugger extensions installer (dotnet-debugger-extensions)](dotnet-debugger-extensions.md) +- [Heap analysis tool (dotnet-gcdump)](dotnet-gcdump.md) diff --git a/docs/core/diagnostics/dotnet-debugger-extensions.md b/docs/core/diagnostics/dotnet-debugger-extensions.md new file mode 100644 index 0000000000000..e38611726e782 --- /dev/null +++ b/docs/core/diagnostics/dotnet-debugger-extensions.md @@ -0,0 +1,80 @@ +--- +title: dotnet-debugger-extensions diagnostic tool - .NET CLI +description: Learn how to install and use the dotnet-debugger-extensions CLI tool to manage the .NET debugging extensions, which is used with native debuggers on Windows and Linux. +ms.date: 11/17/2020 +ms.topic: reference +ms.custom: linux-related-content +--- +# .NET debugger extensions installer (dotnet-debugger-extensions) + +**This article applies to:** ✔️ .NET 6.0 SDK and later versions + +## Install + +To install the latest release version of the `dotnet-debugger-extensions` [NuGet package](https://www.nuget.org/packages/dotnet-debugger-extensions), use the [dotnet tool install](../tools/dotnet-tool-install.md) command: + + ```dotnetcli + dotnet tool install --global dotnet-debugger-extensions + ``` + +## Synopsis + +```console +dotnet-debugger-extensions [-h|--help] [options] [command]] +``` + +## Description + +The `dotnet-debugger-extensions` global tool installs the [.NET debugger extensions](debugger-extensions.md), which enable better debugging experience in native debuggers like WinDbg and LLDB. + +> [!NOTE] +> The Windows Debugger (>= version 10.0.18317.1001 of WinDbg or cdb) automatically loads the extensions from the Microsoft extension gallery. + +## Options + +- **`--version`** + + Displays version information. + +- **`-h|--help`** + + Shows command-line help. + +## dotnet-debugger-extensions install + +Installs the [.NET debugger extensions](debugger-extensions.md) locally for debugging .NET Core processes. On macOS and Linux, the *.lldbinit* file is updated so that the extension automatically loads at LLDB startup. If you're installing on Windows with older debugging tools (before version 10.0.18317.1001), you need to manually load the extension in WinDbg or cdb by running `.load %USERPROFILE%\.dotnet\sos\sos.dll` in the debugger. + +This overwrites any previous installations from the dotnet-debugger-extensions or dotnet-sos installers. + +### Synopsis + +```console +dotnet-debugger-extensions install [--architecture ] +``` + +### Options + +- **`--architecture `** + + Specifies the processor architecture of the extension binaries to install. By default, `dotnet-debugger-extensions` installs the architecture of the host machine. Use this option when you want to install for an architecture that's different from the dotnet host architecture. For example, if you're running Arm32 binaries from an Arm64 host, you need to install with `dotnet-debugger-extensions install --architecture Arm`. + + The following architectures are available: + + - `Arm` + - `Arm64` + - `X86` + - `X64` + +- **`--accept-license-agreement`** + + This option accepts and agrees to the licensing agreement without manual keyboard interaction. For use when console input is redirected. + +## dotnet-debugger-extensions uninstall + +Uninstalls the [.NET debugger extensions](debugger-extensions.md) and, on Linux and macOS, removes it from LLDB configuration. + +### Synopsis + +```console +dotnet-debugger-extensions uninstall +``` diff --git a/docs/core/diagnostics/dotnet-sos.md b/docs/core/diagnostics/dotnet-sos.md index 7b182c9e8485f..03574c83fd307 100644 --- a/docs/core/diagnostics/dotnet-sos.md +++ b/docs/core/diagnostics/dotnet-sos.md @@ -7,7 +7,10 @@ ms.custom: linux-related-content --- # SOS installer (dotnet-sos) -**This article applies to:** ✔️ .NET Core 2.1 SDK and later versions +**This article applies to:** ✔️ .NET 6.0 SDK and later versions + +> [!NOTE] +Consider using the [.NET debugger extensions](dotnet-debugger-extensions.md) instead, which include the SOS functionality and additional features. ## Install @@ -38,10 +41,7 @@ dotnet-sos [-h|--help] [options] [command]] ## Description -The `dotnet-sos` global tool installs the [SOS debugger extension](sos-debugging-extension.md). This extension lets you inspect managed .NET Core state from native debuggers like lldb and windbg. - -> [!NOTE] -> Installing SOS via the `dotnet-sos` tool is only needed on Linux or macOS. It may also be needed on Windows if you're using older debugging tools. Recent versions of the [Windows Debugger](/windows-hardware/drivers/debugger/debugger-download-tools) (>= version 10.0.18317.1001 of WinDbg or cdb) load SOS automatically from the Microsoft extension gallery. +The `dotnet-sos` global tool installs the [SOS debugger extension](sos-debugging-extension.md). This extension lets you inspect managed .NET Core state from native debuggers like LLDB and WinDbg. ## Options @@ -55,7 +55,9 @@ The `dotnet-sos` global tool installs the [SOS debugger extension](sos-debugging ## dotnet-sos install -Installs the [SOS extension](sos-debugging-extension.md) locally for debugging .NET Core processes. On macOS and Linux, the *.lldbinit* file will be updated so that the extension automatically loads at lldb startup. If you're installing SOS on Windows with older debugging tools (prior to version 10.0.18317.1001), you will need to manually load the extension in WinDbg or cdb by running `.load %USERPROFILE%\.dotnet\sos\sos.dll` in the debugger. +Installs the [SOS extension](sos-debugging-extension.md) locally for debugging .NET Core processes. On macOS and Linux, the *.lldbinit* file is updated so that the extension automatically loads at LLDB startup. If you're installing SOS on Windows with older debugging tools (before version 10.0.18317.1001), you need to manually load the extension in WinDbg or cdb by running `.load %USERPROFILE%\.dotnet\sos\sos.dll` in the debugger. + +This overwrites any previous installations from the dotnet-debugger-extensions or dotnet-sos installers. ### Synopsis @@ -67,7 +69,7 @@ dotnet-sos install [--architecture ] - **`--architecture `** - Specifies the processor architecture of the SOS binaries to install. By default, `dotnet-sos` installs the architecture of the host machine. Use this option when you want to install SOS for an architecture that's different from the dotnet host architecture. For example, if you're running Arm32 binaries from an Arm64 host, you will need to install SOS with `dotnet-sos install --architecture Arm`. + Specifies the processor architecture of the SOS binaries to install. By default, `dotnet-sos` installs the architecture of the host machine. Use this option when you want to install SOS for an architecture that's different from the dotnet host architecture. For example, if you're running Arm32 binaries from an Arm64 host, you need to install SOS with `dotnet-sos install --architecture Arm`. The following architectures are available: @@ -78,7 +80,7 @@ dotnet-sos install [--architecture ] ## dotnet-sos uninstall -Uninstalls the [SOS extension](sos-debugging-extension.md) and, on Linux and macOS, removes it from lldb configuration. +Uninstalls the [SOS extension](sos-debugging-extension.md) and, on Linux and macOS, removes it from LLDB configuration. ### Synopsis diff --git a/docs/core/diagnostics/sos-debugging-extension.md b/docs/core/diagnostics/sos-debugging-extension.md index 3f85bdcca865f..b78f1847bce37 100644 --- a/docs/core/diagnostics/sos-debugging-extension.md +++ b/docs/core/diagnostics/sos-debugging-extension.md @@ -32,15 +32,15 @@ The following table of commands is also available under **Help** or **soshelp**. | Command | Description | |---------|-------------| -| **bpmd** [**-nofuturemodule**] [\<*module name*> \<*method name*>] [**-md** <`MethodDesc`>] **-list** **-clear** \<*pending breakpoint number*> **-clearall** | Creates a breakpoint at the specified method in the specified module.

If the specified module and method have not been loaded, this command waits for a notification that the module was loaded and just-in-time (JIT) compiled before creating a breakpoint.

You can manage the list of pending breakpoints by using the **-list**, **-clear**, and **-clearall** options:

The **-list** option generates a list of all the pending breakpoints. If a pending breakpoint has a non-zero module ID, that breakpoint is specific to a function in that particular loaded module. If the pending breakpoint has a zero module ID, that breakpoint applies to modules that have not yet been loaded.

Use the **-clear** or **-clearall** option to remove pending breakpoints from the list. | -| **CLRStack** [**-a**] [**-l**] [**-p**] [**-n**] [**-f**] [**-r**] [**-all**] | Provides a stack trace of managed code only.

The **-p** option shows arguments to the managed function.

The **-l** option shows information on local variables in a frame. The SOS debugging extension cannot retrieve local names, so the output for local names is in the format \<*local address*> **=** \<*value*>.

The **-a** option is a shortcut for **-l** and **-p** combined.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

The **-f** option (full mode) displays the native frames intermixing them with the managed frames and the assembly name and function offset for the managed frames. This option does not display native frames when used with `dotnet-dump`.

The **-r** option dumps the registers for each stack frame.

The **-all** option dumps all the managed threads' stacks. | +| **bpmd** [**-nofuturemodule**] [\<*module name*> \<*method name*>] [**-md** <`MethodDesc`>] **-list** **-clear** \<*pending breakpoint number*> **-clearall** | Creates a breakpoint at the specified method in the specified module.

If the specified module and method have not been loaded, this command waits for a notification that the module was loaded and just-in-time (JIT) compiled before creating a breakpoint.

You can manage the list of pending breakpoints by using the **-list**, **-clear**, and **-clearall** options:

The **-list** option generates a list of all the pending breakpoints. If a pending breakpoint has a nonzero module ID, that breakpoint is specific to a function in that particular loaded module. If the pending breakpoint has a zero module ID, that breakpoint applies to modules that have not yet been loaded.

Use the **-clear** or **-clearall** option to remove pending breakpoints from the list. | +| **CLRStack** [**-a**] [**-l**] [**-p**] [**-n**] [**-f**] [**-r**] [**-all**] | Provides a stack trace of managed code only.

The **-p** option shows arguments to the managed function.

The **-l** option shows information on local variables in a frame. The SOS debugging extension cannot retrieve local names, so the output for local names is in the format \<*local address*> **=** \<*value*>.

The **-a** option is a shortcut for **-l** and **-p** combined.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior.

The **-f** option (full mode) displays the native frames intermixing them with the managed frames and the assembly name and function offset for the managed frames. This option does not display native frames when used with `dotnet-dump`.

The **-r** option dumps the registers for each stack frame.

The **-all** option dumps all the managed threads' stacks. | | **COMState** | Lists the COM apartment model for each thread and a `Context` pointer, if available. This command is only supported on Windows. | | **DumpArray** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-details**] [**-nofields**] \<*array object address*>

-or-

**DA** [**-start** \<*startIndex*>] [**-length** \<*length*>] [**-details**] [**-nofields**] *array object address*> | Examines elements of an array object.

The **-start** option specifies the starting index at which to display elements.

The **-length** option specifies how many elements to show.

The **-details** option displays details of the element using the **DumpObj** and **DumpVC** formats.

The **-nofields** option prevents arrays from displaying. This option is available only when the **-details** option is specified. | -| **DumpAsync** (**dumpasync**) [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [**-waiting**] [**-roots**] | DumpAsync traverses the garbage collected heap and looks for objects representing async state machines as created when an async method's state is transferred to the heap. This command recognizes async state machines defined as `async void`, `async Task`, `async Task`, `async ValueTask`, and `async ValueTask`.

The output includes a block of details for each async state machine object found. These details include:
- A line for the type of the async state machine object, including its MethodTable address, its object address, its size, and its type name.
- A line for the state machine type name as contained in the object.
- A listing of each field on the state machine.
- A line for a continuation from this state machine object, if one or more has been registered.
- Discovered GC roots for this async state machine object. | +| **DumpAsync** (**dumpasync**) [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [**-waiting**] [**-roots**] | DumpAsync traverses the garbage collected heap and looks for objects representing async state machines as created when an async method's state is transferred to the heap. This command recognizes async state machines defined as `async void`, `async Task`, `async Task`, `async ValueTask`, and `async ValueTask`.

The output includes a block of details for each async state machine object found. These details include:
- A line for the type of the async state machine object, including its MethodTable address, its object address, its size, and its type name.
- A line for the state machine type name as contained in the object.
- A listing of each field on the state machine.
- A line for a continuation from this state machine object, if one or more has been registered.
- Discovered GC roots for this async state machine object. | | **DumpAssembly** \<*assembly address*> | Displays information about an assembly.

The **DumpAssembly** command lists multiple modules, if they exist.

You can get an assembly address by using the **DumpDomain** command. | | **DumpClass** \<*EEClass address*> | Displays information about the `EEClass` structure associated with a type.

The **DumpClass** command displays static field values but does not display nonstatic field values.

Use the **DumpMT**, **DumpObj**, **Name2EE**, or **Token2EE** command to get an `EEClass` structure address. | -| **DumpDomain** [\<*domain address*>] | Enumerates each object that's loaded within the specified object address. When called with no parameters, the **DumpDomain** command lists all objects in a process. Since .NET (Core) only has one , **DumpDomain** only returns one object. | -| **DumpHeap** [**-stat**] [**-strings**] [**-short**] [**-min** \<*size*>] [**-max** \<*size*>] [**-thinlock**] [**-startAtLowerBound**] [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [*start* [*end*]] | Displays information about the garbage-collected heap and collection statistics about objects.

The **DumpHeap** command displays a warning if it detects excessive fragmentation in the garbage collector heap.

The **-stat** option restricts the output to the statistical type summary.

The **-strings** option restricts the output to a statistical string value summary.

The **-short** option limits output to just the address of each object. This lets you easily pipe output from the command to another debugger command for automation.

The **-min** option ignores objects that are less than the `size` parameter, specified in bytes.

The **-max** option ignores objects that are larger than the `size` parameter, specified in bytes.

The **-thinlock** option reports ThinLocks. For more information, see the **SyncBlk** command.

The `-startAtLowerBound` option forces the heap walk to begin at the lower bound of a supplied address range. During the planning phase, the heap is often not walkable because objects are being moved. This option forces **DumpHeap** to begin its walk at the specified lower bound. You must supply the address of a valid object as the lower bound for this option to work. You can display memory at the address of a bad object to manually find the next method table. If the garbage collection is currently in a call to `memcopy`, you may also be able to find the address of the next object by adding the size to the start address, which is supplied as a parameter.

The **-mt** option lists only those objects that correspond to the specified `MethodTable` structure.

The **-type** option lists only those objects whose type name is a substring match of the specified string.

The `start` parameter begins listing from the specified address.

The `end` parameter stops listing at the specified address. | +| **DumpDomain** [\<*domain address*>] | Enumerates each object that's loaded within the specified object address. When called with no parameters, the **DumpDomain** command lists all objects in a process. Since .NET (Core) only has one , **DumpDomain** only returns one object. | +| **DumpHeap** [**-stat**] [**-strings**] [**-short**] [**-min** \<*size*>] [**-max** \<*size*>] [**-thinlock**] [**-startAtLowerBound**] [**-mt** \<*MethodTable address*>] [**-type** \<*partial type name*>] [*start* [*end*]] | Displays information about the garbage-collected heap and collection statistics about objects.

The **DumpHeap** command displays a warning if it detects excessive fragmentation in the garbage collector heap.

The **-stat** option restricts the output to the statistical type summary.

The **-strings** option restricts the output to a statistical string value summary.

The **-short** option limits output to just the address of each object. This lets you easily pipe output from the command to another debugger command for automation.

The **-min** option ignores objects that are less than the `size` parameter, specified in bytes.

The **-max** option ignores objects that are larger than the `size` parameter, specified in bytes.

The **-thinlock** option reports ThinLocks. For more information, see the **SyncBlk** command.

The `-startAtLowerBound` option forces the heap walk to begin at the lower bound of a supplied address range. During the planning phase, the heap is often not walkable because objects are being moved. This option forces **DumpHeap** to begin its walk at the specified lower bound. You must supply the address of a valid object as the lower bound for this option to work. You can display memory at the address of a bad object to manually find the next method table. If the garbage collection is currently in a call to `memcopy`, you may also be able to find the address of the next object by adding the size to the start address, which is supplied as a parameter.

The **-mt** option lists only those objects that correspond to the specified `MethodTable` structure.

The **-type** option lists only those objects whose type name is a substring match of the specified string.

The `start` parameter begins listing from the specified address.

The `end` parameter stops listing at the specified address. | | **DumpIL** \<*Managed DynamicMethod object*> | \<*DynamicMethodDesc pointer*> | \<*MethodDesc pointer*> | Displays the common intermediate language (CIL) that's associated with a managed method.

Dynamic CIL is emitted differently than CIL that's loaded from an assembly. Dynamic CIL refers to objects in a managed object array rather than to metadata tokens. | | **DumpLog** [**-addr** \<*addressOfStressLog*>] [<*Filename*>] | Writes the contents of an in-memory stress log to the specified file. If you do not specify a name, this command creates a file called StressLog.txt in the current directory.

The in-memory stress log helps you diagnose stress failures without using locks or I/O. To enable the stress log, set the following registry keys under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\\.NETFramework:

(DWORD) StressLog = 1

(DWORD) LogFacility = 0xffffffff

(DWORD) StressLogSize = 65536

The optional `-addr` option lets you specify a stress log other than the default log. | | **DumpMD** \<*MethodDesc address*> | Displays information about a `MethodDesc` structure at the specified address.

You can use the **IP2MD** command to get the `MethodDesc` structure address from a managed function. | @@ -51,12 +51,12 @@ The following table of commands is also available under **Help** or **soshelp**. | **DumpStack** [**-EE**] [**-n**] [`top` *stack* [`bottom` *stack*]] | Displays a stack trace.

The **-EE** option causes the **DumpStack** command to display only managed functions. Use the `top` and `bottom` parameters to limit the stack frames displayed on x86 platforms.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS will look up the symbols for every managed frame and if successful will display the corresponding source file name and line number. The **-n** (No line numbers) parameter can be specified to disable this behavior. | | **DumpSig** \<*sigaddr*> \<*moduleaddr*> | Displays information about a `Sig` structure at the specified address. | | **DumpSigElem** \<*sigaddr*> \<*moduleaddr*> | Displays a single element of a signature object. In most cases, you should use **DumpSig** to look at individual signature objects. However, if a signature has been corrupted in some way, you can use **DumpSigElem** to read the valid portions of it. | -| **DumpStackObjects** [**-verify**] [`top` *stack* [`bottom` *stack*]]

-or-

**DSO** [**-verify**] [`top` *stack* [`bottom` *stack*]] | Displays all managed objects found within the bounds of the current stack.

The **-verify** option validates each non-static `CLASS` field of an object field.

Use the **DumpStackObject** command with stack tracing commands such as **K** (windbg) or **bt** (lldb) along with the **clrstack** command to determine the values of local variables and parameters. | +| **DumpStackObjects** [**-verify**] [`top` *stack* [`bottom` *stack*]]

-or-

**DSO** [**-verify**] [`top` *stack* [`bottom` *stack*]] | Displays all managed objects found within the bounds of the current stack.

The **-verify** option validates each nonstatic `CLASS` field of an object field.

Use the **DumpStackObject** command with stack tracing commands such as **K** (windbg) or **bt** (lldb) along with the **clrstack** command to determine the values of local variables and parameters. | | **DumpVC** \<*MethodTable address*> \<*Address*> | Displays information about the fields of a value class at the specified address.

The **MethodTable** parameter allows the **DumpVC** command to correctly interpret fields. Value classes do not have a method table as their first field. | -| **EEHeap** [**-gc**] [**-loader**] | Displays information about process memory consumed by internal runtime data structures.

The **-gc** and **-loader** options limit the output of this command to garbage collector or loader data structures.

The information for the garbage collector lists the ranges of each segment in the managed heap. If the pointer falls within a segment range given by **-gc**, the pointer is an object pointer. | +| **EEHeap** [**-gc**] [**-loader**] | Displays information about process memory consumed by internal runtime data structures.

The **-gc** and **-loader** options limit the output of this command to garbage collector or loader data structures.

The information for the garbage collector lists the ranges of each segment in the managed heap. If the pointer falls within a segment range given by **-gc**, the pointer is an object pointer. | | **EEStack** [**-short**] [**-EE**] | Runs the **DumpStack** command on all threads in the process.

The **-EE** option is passed directly to the **DumpStack** command. The **-short** parameter limits the output to the following kinds of threads:

Threads that have taken a lock.

Threads that have been stalled in order to allow a garbage collection.

Threads that are currently in managed code. | -| **EHInfo** [\<*MethodDesc address*>] [\<*Code address*>] | Displays the exception handling blocks in a specified method. This command displays the code addresses and offsets for the clause block (the `try` block) and the handler block (the `catch` block). | -| **FAQ** | Displays frequently asked questions. Not supported in `dotnet-dump`. | +| **EHInfo** [\<*MethodDesc address*>] [\<*Code address*>] | Displays the exception handling blocks in a specified method. This command displays the code addresses and offsets for the clause block (the `try` block) and the handler block (the `catch` block). | +| **FAQ** | Displays frequently asked questions. Not supported in `dotnet-dump`. | | **FinalizeQueue** [**-detail**] | [**-allReady**] [**-short**] | Displays all objects registered for finalization.

The **-detail** option displays extra information about any `SyncBlocks` that need to be cleaned up, and any `RuntimeCallableWrappers` (RCWs) that await cleanup. Both of these data structures are cached and cleaned up by the finalizer thread when it runs.

The `-allReady` option displays all objects that are ready for finalization, regardless of whether they are already marked by the garbage collection as such, or will be marked by the next garbage collection. The objects that are in the "ready for finalization" list are finalizable objects that are no longer rooted. This option can be very expensive, because it verifies whether all the objects in the finalizable queues are still rooted.

The `-short` option limits the output to the address of each object. If it is used in conjunction with **-allReady**, it enumerates all objects that have a finalizer that are no longer rooted. If it is used independently, it lists all objects in the finalizable and "ready for finalization" queues. | | **FindAppDomain** \<*Object address*> | Determines the application domain of an object at the specified address. | | **FindRoots** **-gen** \<*N*> | **-gen any** |\<*object address*> | Causes the debugger to break in the debuggee on the next collection of the specified generation. The effect is reset as soon as the break occurs. To break on the next collection, you have to reissue the command. The *\* form of this command is used after the break caused by the **-gen** or **-gen any** has occurred. At that time, the debuggee is in the right state for **FindRoots** to identify roots for objects from the current condemned generations. Only supported on Windows. | @@ -86,9 +86,9 @@ The following table of commands is also available under **Help** or **soshelp**. | **SOSFlush** | Flushes an internal SOS cache. | | **SOSStatus** [**-reset**] | Displays internal SOS status or reset the internal cached state. | | **StopOnException** [**-derived**] [**-create** | **-create2**] \<*Exception*> \<*Pseudo-register number*> | Causes the debugger to stop when the specified exception is thrown, but to continue running when other exceptions are thrown.

The **-derived** option catches the specified exception and every exception that derives from the specified exception.

Only supported with Windbg. | -| **SyncBlk** [**-all** | \<*syncblk number*>] | Displays the specified `SyncBlock` structure or all `SyncBlock` structures. If you do not pass any arguments, the **SyncBlk** command displays the `SyncBlock` structure corresponding to objects that are owned by a thread.

A `SyncBlock` structure is a container for extra information that does not need to be created for every object. It can hold COM interop data, hash codes, and locking information for thread-safe operations. | +| **SyncBlk** [**-all** | \<*syncblk number*>] | Displays the specified `SyncBlock` structure or all `SyncBlock` structures. If you do not pass any arguments, the **SyncBlk** command displays the `SyncBlock` structure corresponding to objects that are owned by a thread.

A `SyncBlock` structure is a container for extra information that does not need to be created for every object. It can hold COM interop data, hash codes, and locking information for thread-safe operations. | | **ThreadPool** | Displays information about the managed thread pool, including the number of work requests in the queue, the number of completion port threads, and the number of timers. | -| **Threads** (**clrthreads**) [**-live**] [**-special**] | Displays all managed threads in the process.

The **Threads** command displays the debugger shorthand ID, the CLR thread ID, and the operating system thread ID. Additionally, the **Threads** command displays a Domain column that indicates the application domain in which a thread is executing, an APT column that displays the COM apartment mode, and an Exception column that displays the last exception thrown in the thread.

The **-live** option displays threads associated with a live thread.

The **-special** option displays all special threads created by the CLR. Special threads include garbage collection threads (in concurrent and server garbage collection), debugger helper threads, finalizer threads, unload threads, and thread pool timer threads. | +| **Threads** (**clrthreads**) [**-live**] [**-special**] | Displays all managed threads in the process.

The **Threads** command displays the debugger shorthand ID, the CLR thread ID, and the operating system thread ID. Additionally, the **Threads** command displays a Domain column that indicates the application domain in which a thread is executing, an APT column that displays the COM apartment mode, and an Exception column that displays the last exception thrown in the thread.

The **-live** option displays threads associated with a live thread.

The **-special** option displays all special threads created by the CLR. Special threads include garbage collection threads (in concurrent and server garbage collection), debugger helper threads, finalizer threads, unload threads, and thread pool timer threads. | | **ThreadState \<** *State value field* **>** | Displays the state of the thread. The `value` parameter is the value of the `State` field in the **Threads** report output. | | **Token2EE** \<*module name*> \<*token*> | Turns the specified metadata token in the specified module into a `MethodTable` structure or `MethodDesc` structure.

You can pass `*` for the module name parameter to find what that token maps to in every loaded managed module. You can also pass the debugger's name for a module, such as `mscorlib` or `image00400000`. | | **U** [**-gcinfo**] [**-ehinfo**] [**-n**] \<*MethodDesc address*> | \<*Code address*> | Displays an annotated disassembly of a managed method specified either by a `MethodDesc` structure pointer for the method or by a code address within the method body. The **U** command displays the entire method from start to finish, with annotations that convert metadata tokens to names.

The **-gcinfo** option causes the **U** command to display the `GCInfo` structure for the method.

The **-ehinfo** option displays exception information for the method. You can also obtain this information with the **EHInfo** command.

The **-n** option disables the display of source file names and line numbers. If the debugger has the option SYMOPT_LOAD_LINES specified, SOS looks up the symbols for every managed frame and, if successful, displays the corresponding source file name and line number. You can specify the **-n** option to disable this behavior. | @@ -105,8 +105,6 @@ For a list of available SOS commands with `dotnet-dump analyze`, see [dotnet-dum You can also use the SOS debugging extension by loading it into the [WinDbg/dbg debugger](/windows-hardware/drivers/debugger/debugger-download-tools) and executing commands within the Windows debugger. SOS commands can be used on live processes or dumps. -Windbg should load the SOS extension automatically whenever the process being debugged contains the .NET runtime (coreclr.dll or libcoreclr.so). - ### LLDB Debugger For instructions on configuring SOS for LLDB, see [dotnet-sos](dotnet-sos.md). SOS commands can be used on live processes or dumps. @@ -208,7 +206,7 @@ By default you can reach all the SOS commands by entering: `sos [command_name]`. | `!DumpVC 0090320c 00a79d9c` | Displays the fields of a value class at the address `00a79d9c` using the method table at the address `0090320c`. | | `!eeheap` -gc | Displays the process memory used by the garbage collector. | | `!finalizequeue` | Displays all objects scheduled for finalization. | -| `!findappdomain 00a79d98` | Determines the application domain of an object at the address `00a79d98`. | +| `!findappdomain 00a79d98` | Determines the application domain of an object at the address `00a79d98`. | | `!gcinfo 5b68dbb8` | Displays all garbage collector handles in the current process. | | `!name2ee unittest.exe MainClass.Main` | Displays the `MethodTable` and `EEClass` structures for the `Main` method in the class `MainClass` in the module `unittest.exe`. | | `!token2ee unittest.exe 02000003` | Displays information about the metadata token at the address `02000003` in the module `unittest.exe`. | @@ -217,7 +215,7 @@ By default you can reach all the SOS commands by entering: `sos [command_name]`. | Command | Description | | - | - | -| `dumparray -start 2 -length 5 -details 00ad28d0` | Displays the contents of an array at the address `00ad28d0`. The display starts from the second element and continues for five elements. | +| `dumparray -start 2 -length 5 -details 00ad28d0` | Displays the contents of an array at the address `00ad28d0`. The display starts from the second element and continues for five elements. | | `dumpassembly 1ca248` | Displays the contents of an assembly at the address `1ca248`. | | `dumpheap` | Displays information about the garbage collector heap. | | `dumplog` | Writes the contents of the in-memory stress log to a (default) file called StressLog.txt in the current directory. | diff --git a/docs/core/diagnostics/tools-overview.md b/docs/core/diagnostics/tools-overview.md index 43a87d25cd4d2..5ab847b2ccacb 100644 --- a/docs/core/diagnostics/tools-overview.md +++ b/docs/core/diagnostics/tools-overview.md @@ -50,9 +50,9 @@ The [dotnet-stack](dotnet-stack.md) tool allows you to quickly print the managed [dotnet-symbol](dotnet-symbol.md) downloads files (for example, symbols, DAC/DBI, and host files) needed to open a core dump or minidump. Use this tool if you need symbols and modules to debug a dump file captured on a different machine. -### dotnet-sos +### dotnet-debugger-extensions -[dotnet-sos](dotnet-sos.md) installs the [SOS debugging extension](sos-debugging-extension.md) on Linux and macOS (and on Windows if you're using [Windbg/cdb](/windows-hardware/drivers/debugger/debugger-download-tools)). +[dotnet-debugger-extensions](dotnet-debugger-extensions.md) installs the [.NET debugger extensions](debugger-extensions.md) on Linux, macOS, and Windows. [LLDB](https://lldb.llvm.org/) is required for Linux and macOS, and [Windbg/cdb](/windows-hardware/drivers/debugger/debugger-download-tools) is needed for Windows. ## Other tools diff --git a/docs/core/testing/mstest-analyzers/mstest0021.md b/docs/core/testing/mstest-analyzers/mstest0021.md index e2d0966d0da8c..d534184a7a661 100644 --- a/docs/core/testing/mstest-analyzers/mstest0021.md +++ b/docs/core/testing/mstest-analyzers/mstest0021.md @@ -22,7 +22,7 @@ ms.author: amauryleve | **Enabled by default** | No | | **Default severity** | Info | | **Introduced in version** | 3.4.0 | -| **Is there a code fix** | No | +| **Is there a code fix** | Yes, starting with 3.7.0 | ## Cause diff --git a/docs/core/testing/unit-testing-platform-config.md b/docs/core/testing/unit-testing-platform-config.md index 4d6c48217a1d0..54c21fad0c2e5 100644 --- a/docs/core/testing/unit-testing-platform-config.md +++ b/docs/core/testing/unit-testing-platform-config.md @@ -29,6 +29,8 @@ The platform will automatically detect and load the *[appname].testconfig.json* When using [Microsoft.Testing.Platform.MSBuild](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild), you can simply create a *testconfig.json* file that will be automatically renamed to *[appname].testconfig.json* and moved to the output directory of the test project. +Starting with Microsoft.Testing.Platform 1.5, you can use the command-line argument `--config-file` to specify the path to the *testconfig.json*. This file takes precedence over the *[appname].testconfig.json* file. + > [!NOTE] > The *[appname].testconfig.json* file will get overwritten on subsequent builds. @@ -38,3 +40,7 @@ Environment variables can be used to supply some runtime configuration informati > [!NOTE] > Environment variables take precedence over configuration settings in the *testconfig.json* file. + +### `TESTINGPLATFORM_UI_LANGUAGE` environment variable + +Starting with Microsoft.Testing.Platform 1.5, this environment variable sets the language of the platform for displaying messages and logs using a locale value such as `en-us`. This language takes precedence over the Visual Studio and .NET SDK languages. The supported values are the same as for Visual Studio. For more information, see the section on changing the installer language in the [Visual Studio installation documentation](/visualstudio/install/install-visual-studio). diff --git a/docs/core/testing/unit-testing-platform-extensions-fakes.md b/docs/core/testing/unit-testing-platform-extensions-fakes.md index d702475bf0d63..ac5a1ae7f582f 100644 --- a/docs/core/testing/unit-testing-platform-extensions-fakes.md +++ b/docs/core/testing/unit-testing-platform-extensions-fakes.md @@ -25,3 +25,5 @@ To use the new extension with an existing project, update the existing `Microsof - + ``` + +If you are using MSTest.Sdk 3.7 or later, use the `EnableMicrosoftTestingExtensionsFakes` property to enable the extension and don't add the package reference. diff --git a/docs/core/testing/unit-testing-platform-intro.md b/docs/core/testing/unit-testing-platform-intro.md index 26edb0c032be2..16504ec473507 100644 --- a/docs/core/testing/unit-testing-platform-intro.md +++ b/docs/core/testing/unit-testing-platform-intro.md @@ -183,56 +183,95 @@ To run a test project in CI add one step for each test executable that you wish The list below described only the platform options. To see the specific options brought by each extension, either refer to the extension documentation page or use the `--help` option. +- **`@`** + + Specifies the name of the response file. The response file name must immediately follow the @ character with no white space between the @ character and the response file name. + + Options in a response file are interpreted as if they were present at that place in the command line. Each argument in a response file must begin and end on the same line. You cannot use the backslash character (\) to concatenate lines. Using a response file helps for very long commands that might exceed the terminal limits. You can combine a response file with inline command-line arguments. For example: + + ```console + ./TestExecutable.exe @"filter.rsp" --timeout 10s + ``` + + where *filter.rsp* can have the following contents: + + ```rsp + --filter "A very long filter" + ``` + + Or a single rsp file can be used to specify both timeout and filter as follows: + + ```console + ./TestExecutable.exe @"arguments.rsp" + ``` + + ```rsp + --filter "A very long filter" + --timeout 10s + ``` + +- **`--config-file`** + + Specifies a [*testconfig.json*](unit-testing-platform-config.md) file. + - **`--diagnostic`** -Enables the diagnostic logging. The default log level is `Trace`. The file is written in the output directory with the following name format, `log_[MMddHHssfff].diag`. + Enables the diagnostic logging. The default log level is `Trace`. The file is written in the output directory with the following name format, `log_[MMddHHssfff].diag`. - **`--diagnostic-filelogger-synchronouswrite`** -Forces the built-in file logger to synchronously write logs. Useful for scenarios where you don't want to lose any log entries (if the process crashes). This does slow down the test execution. + Forces the built-in file logger to synchronously write logs. Useful for scenarios where you don't want to lose any log entries (if the process crashes). This does slow down the test execution. - **`--diagnostic-output-directory`** -The output directory of the diagnostic logging, if not specified the file is generated in the default _TestResults_ directory. + The output directory of the diagnostic logging, if not specified the file is generated in the default _TestResults_ directory. - **`--diagnostic-output-fileprefix`** -The prefix for the log file name. Defaults to `"log_"`. + The prefix for the log file name. Defaults to `"log_"`. - **`--diagnostic-verbosity`** -Defines the verbosity level when the `--diagnostic` switch is used. The available values are `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. + Defines the verbosity level when the `--diagnostic` switch is used. The available values are `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. + +- **`--exit-on-process-exit`** + + Exit the test process if dependent process exits. PID must be provided. - **`--help`** -Prints out a description of how to use the command. + Prints out a description of how to use the command. - **`-ignore-exit-code`** -Allows some non-zero exit codes to be ignored, and instead returned as `0`. For more information, see [Ignore specific exit codes](./unit-testing-platform-exit-codes.md#ignore-specific-exit-codes). + Allows some non-zero exit codes to be ignored, and instead returned as `0`. For more information, see [Ignore specific exit codes](./unit-testing-platform-exit-codes.md#ignore-specific-exit-codes). - **`--info`** -Displays advanced information about the .NET Test Application such as: + Displays advanced information about the .NET Test Application such as: -- The platform. -- The environment. -- Each registered command line provider, such as its, `name`, `version`, `description` and `options`. -- Each registered tool, such as its, `command`, `name`, `version`, `description`, and all command line providers. + - The platform. + - The environment. + - Each registered command line provider, such as its `name`, `version`, `description`, and `options`. + - Each registered tool, such as its `command`, `name`, `version`, `description`, and all command-line providers. -This feature is used to understand extensions that would be registering the same command line option or the changes in available options between multiple versions of an extension (or the platform). + This feature is used to understand extensions that would be registering the same command line option or the changes in available options between multiple versions of an extension (or the platform). - **`--list-tests`** -List available tests. Tests will not be executed. + List available tests. Tests will not be executed. - **`--minimum-expected-tests`** -Specifies the minimum number of tests that are expected to run. By default, at least one test is expected to run. + Specifies the minimum number of tests that are expected to run. By default, at least one test is expected to run. - **`--results-directory`** -The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is `TestResults` in the directory that contains the test application. + The directory where the test results are going to be placed. If the specified directory doesn't exist, it's created. The default is `TestResults` in the directory that contains the test application. + +- **`--timeout`** + + A global test execution timeout. Takes one argument as string in the format `[h|m|s]` where `` is float. ## MSBuild integration diff --git a/docs/core/tutorials/top-level-templates.md b/docs/core/tutorials/top-level-templates.md index 8efaa283852ea..f0b9e2f415ff2 100644 --- a/docs/core/tutorials/top-level-templates.md +++ b/docs/core/tutorials/top-level-templates.md @@ -1,7 +1,7 @@ --- title: "C# console app template changes in .NET 6+" description: The .NET 6+ project template for C# console apps uses top-level statements. Understand what changed and how to use existing learning materials with the new syntax. -ms.date: 03/15/2024 +ms.date: 11/22/2024 --- # C# console app template generates top-level statements @@ -34,7 +34,7 @@ namespace MyApp In the preceding code, the actual namespace depends on the project name. -These two forms represent the same program. Both are valid with C# 10.0. When you use the newer version, you only need to write the body of the `Main` method. The compiler generates a `Program` class with an entry point method and places all your top level statements in that method. The name of the generated method isn't `Main`, it's an implementation detail that your code can't reference directly. You don't need to include the other program elements, the compiler generates them for you. You can learn more about the code the compiler generates when you use top level statements in the article on [top level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) in the C# Guide's fundamentals section. +These two forms represent the same program. Both are valid in C#. When you use the newer version, you only need to write the body of the `Main` method. The compiler generates a `Program` class with an entry point method and places all your top level statements in that method. The name of the generated method isn't `Main`, it's an implementation detail that your code can't reference directly. You don't need to include the other program elements, the compiler generates them for you. You can learn more about the code the compiler generates when you use top level statements in the article on [top level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) in the C# Guide's fundamentals section. You have two options to work with tutorials that aren't updated to use .NET 6+ templates: diff --git a/docs/core/tutorials/with-visual-studio-code.md b/docs/core/tutorials/with-visual-studio-code.md index ffe7a95b42b65..2e19d3c1df6c8 100644 --- a/docs/core/tutorials/with-visual-studio-code.md +++ b/docs/core/tutorials/with-visual-studio-code.md @@ -1,7 +1,7 @@ --- title: Create a .NET console application using Visual Studio Code description: Learn how to create a .NET console application using Visual Studio Code. -ms.date: 09/12/2024 +ms.date: 11/22/2024 zone_pivot_groups: dotnet-version --- # Tutorial: Create a .NET console application using Visual Studio Code @@ -168,7 +168,7 @@ Create a .NET console app project named "HelloWorld". The code defines a class, `Program`, with a single method, `Main`, that takes a array as an argument. `Main` is the application entry point, the method that's called automatically by the runtime when it launches the application. Any command-line arguments supplied when the application is launched are available in the *args* array. - In the latest version of C#, a new feature named [top-level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) lets you omit the `Program` class and the `Main` method. Most existing C# programs don't use top-level statements, so this tutorial doesn't use this new feature. But it's available in C# 10, and whether you use it in your programs is a matter of style preference. + In the latest version of C#, a new feature named [top-level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) lets you omit the `Program` class and the `Main` method. Most existing C# programs don't use top-level statements, so this tutorial doesn't use this new feature. But it's the current default for new C# programs. Whether you use it in your programs is a matter of style preference. ## Run the app diff --git a/docs/core/tutorials/with-visual-studio.md b/docs/core/tutorials/with-visual-studio.md index b5effd40328ab..7be66a27958f1 100644 --- a/docs/core/tutorials/with-visual-studio.md +++ b/docs/core/tutorials/with-visual-studio.md @@ -1,7 +1,7 @@ --- title: Create a .NET console application using Visual Studio description: Learn how to create a .NET console application with C# or Visual Basic using Visual Studio. -ms.date: 03/21/2024 +ms.date: 11/22/2024 zone_pivot_groups: dotnet-version dev_langs: - "csharp" @@ -186,7 +186,7 @@ Create a .NET console app project named "HelloWorld". The code defines a class, `Program`, with a single method, `Main`, that takes a array as an argument. `Main` is the application entry point, the method that's called automatically by the runtime when it launches the application. Any command-line arguments supplied when the application is launched are available in the *args* array. - In the latest version of C#, a new feature named [top-level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) lets you omit the `Program` class and the `Main` method. Most existing C# programs don't use top-level statements, so this tutorial doesn't use this new feature. But it's available in C# 10, and whether you use it in your programs is a matter of style preference. + In the latest version of C#, a new feature named [top-level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) lets you omit the `Program` class and the `Main` method. Most existing C# programs don't use top-level statements, so this tutorial doesn't use this new feature. But it's the current default for new C# programs. Whether you use it in your programs is a matter of style preference. ## Run the app @@ -306,7 +306,7 @@ Create a .NET console app project named "HelloWorld". The code defines a class, `Program`, with a single method, `Main`, that takes a array as an argument. `Main` is the application entry point, the method that's called automatically by the runtime when it launches the application. Any command-line arguments supplied when the application is launched are available in the *args* array. - In the latest version of C#, a new feature named [top-level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) lets you omit the `Program` class and the `Main` method. Most existing C# programs don't use top-level statements, so this tutorial doesn't use this new feature. But it's available in C# 10, and whether you use it in your programs is a matter of style preference. + In the latest version of C#, a new feature named [top-level statements](../../csharp/fundamentals/program-structure/top-level-statements.md) lets you omit the `Program` class and the `Main` method. Most existing C# programs don't use top-level statements, so this tutorial doesn't use this new feature. But it's the current default for new C# programs. Whether you use it in your programs is a matter of style preference. ## Run the app diff --git a/docs/core/whats-new/dotnet-6.md b/docs/core/whats-new/dotnet-6.md index 7abc477c45ee4..ab3ee451ffb72 100644 --- a/docs/core/whats-new/dotnet-6.md +++ b/docs/core/whats-new/dotnet-6.md @@ -10,7 +10,7 @@ author: gewarren .NET 6 delivers the final parts of the .NET unification plan that started with [.NET 5](dotnet-5.md). .NET 6 unifies the SDK, base libraries, and runtime across mobile, desktop, IoT, and cloud apps. In addition to this unification, the .NET 6 ecosystem offers: -- **Simplified development**: Getting started is easy. New language features in [C# 10](../../csharp/whats-new/csharp-10.md) reduce the amount of code you need to write. And investments in the web stack and minimal APIs make it easy to quickly write smaller, faster microservices. +- **Simplified development**: Getting started is easy. New language features in [C# 10](../../csharp/whats-new/csharp-version-history.md#c-version-10) reduce the amount of code you need to write. And investments in the web stack and minimal APIs make it easy to quickly write smaller, faster microservices. - **Better performance**: .NET 6 is the fastest full stack web framework, which lowers compute costs if you're running in the cloud. @@ -54,7 +54,7 @@ The .NET 6 release includes support for macOS Arm64 (or "Apple Silicon") and Win ## C# 10 and templates -C# 10 includes innovations such as `global using` directives, file-scoped namespace declarations, and record structs. For more information, see [What's new in C# 10](../../csharp/whats-new/csharp-10.md). +C# 10 includes innovations such as `global using` directives, file-scoped namespace declarations, and record structs. For more information, see [C# Language version history](../../csharp/whats-new/csharp-version-history.md#c-version-10). In concert with that work, the .NET SDK project templates for C# have been modernized to use some of the new language features: @@ -273,7 +273,6 @@ The new class represents a c ## See also -- [What's new in C# 10](../../csharp/whats-new/csharp-10.md) - [What's new in F# 6](../../fsharp/whats-new/fsharp-6.md) - [What's new in EF Core 6](/ef/core/what-is-new/ef-core-6.0/whatsnew) - [What's new in ASP.NET Core 6](/aspnet/core/release-notes/aspnetcore-6.0) diff --git a/docs/csharp/whats-new/tutorials/interpolated-string-handler.md b/docs/csharp/advanced-topics/performance/interpolated-string-handler.md similarity index 67% rename from docs/csharp/whats-new/tutorials/interpolated-string-handler.md rename to docs/csharp/advanced-topics/performance/interpolated-string-handler.md index 109f457a980db..8999715a0a752 100644 --- a/docs/csharp/whats-new/tutorials/interpolated-string-handler.md +++ b/docs/csharp/advanced-topics/performance/interpolated-string-handler.md @@ -1,11 +1,11 @@ --- title: Explore C# string interpolation handlers description: This advanced tutorial shows how you can write a custom string interpolation handler that hooks into the runtime processing of an interpolated string. -ms.date: 03/24/2022 +ms.date: 11/22/2024 --- # Tutorial: Write a custom string interpolation handler -In this tutorial, you'll learn how to: +In this tutorial, you learn how to: > [!div class="checklist"] > @@ -16,20 +16,18 @@ In this tutorial, you'll learn how to: ## Prerequisites -You'll need to set up your machine to run .NET 6, including the C# 10 compiler. The C# 10 compiler is available starting with [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) or [.NET 6 SDK](https://dotnet.microsoft.com/download). +You need to set up your machine to run .NET. The C# compiler is available with [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) or the [.NET SDK](https://dotnet.microsoft.com/download). This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or the .NET CLI. -## New outline - -C# 10 adds support for a custom [*interpolated string handler*](#implement-the-handler-pattern). An interpolated string handler is a type that processes the placeholder expression in an interpolated string. Without a custom handler, placeholders are processed similar to . Each placeholder is formatted as text, and then the components are concatenated to form the resulting string. +You can write a custom [*interpolated string handler*](#implement-the-handler-pattern). An interpolated string handler is a type that processes the placeholder expression in an interpolated string. Without a custom handler, placeholders are processed similar to . Each placeholder is formatted as text, and then the components are concatenated to form the resulting string. You can write a handler for any scenario where you use information about the resulting string. Will it be used? What constraints are on the format? Some examples include: -- You may require none of the resulting strings are greater than some limit, such as 80 characters. You can process the interpolated strings to fill a fixed-length buffer, and stop processing once that buffer length is reached. -- You may have a tabular format, and each placeholder must have a fixed length. A custom handler can enforce that, rather than forcing all client code to conform. +- You might require none of the resulting strings are greater than some limit, such as 80 characters. You can process the interpolated strings to fill a fixed-length buffer, and stop processing once that buffer length is reached. +- You might have a tabular format, and each placeholder must have a fixed length. A custom handler can enforce that, rather than forcing all client code to conform. -In this tutorial, you'll create a string interpolation handler for one of the core performance scenarios: logging libraries. Depending on the configured log level, the work to construct a log message isn't needed. If logging is off, the work to construct a string from an interpolated string expression isn't needed. The message is never printed, so any string concatenation can be skipped. In addition, any expressions used in the placeholders, including generating stack traces, doesn't need to be done. +In this tutorial, you create a string interpolation handler for one of the core performance scenarios: logging libraries. Depending on the configured log level, the work to construct a log message isn't needed. If logging is off, the work to construct a string from an interpolated string expression isn't needed. The message is never printed, so any string concatenation can be skipped. In addition, any expressions used in the placeholders, including generating stack traces, doesn't need to be done. An interpolated string handler can determine if the formatted string will be used, and only perform the necessary work if needed. @@ -39,7 +37,7 @@ Let's start from a basic `Logger` class that supports different levels: :::code language="csharp" source="./snippets/interpolated-string-handler/Logger-v1.cs" id="InitialLogger"::: -This `Logger` supports six different levels. When a message won't pass the log level filter, there's no output. The public API for the logger accepts a (fully formatted) string as the message. All the work to create the string has already been done. +This `Logger` supports six different levels. When a message doesn't pass the log level filter, there's no output. The public API for the logger accepts a (fully formatted) string as the message. All the work to create the string has already been done. ## Implement the handler pattern @@ -58,7 +56,7 @@ You can now add an overload to `LogMessage` in the `Logger` class to try your ne :::code language="csharp" source="./snippets/interpolated-string-handler/Logger-v2.cs" id="LogMessageOverload"::: -You don't need to remove the original `LogMessage` method, the compiler will prefer a method with an interpolated handler parameter over a method with a `string` parameter when the argument is an interpolated string expression. +You don't need to remove the original `LogMessage` method, the compiler prefers a method with an interpolated handler parameter over a method with a `string` parameter when the argument is an interpolated string expression. You can verify that the new handler is invoked using the following code as the main program: @@ -93,9 +91,13 @@ Tracing through the output, you can see how the compiler adds code to call the h Finally, notice that the last warning doesn't invoke the interpolated string handler. The argument is a `string`, so that call invokes the other overload with a string parameter. +> [!IMPORTANT] +> +> The version of the `Logger` for this section is a `ref struct`. A `ref struct` minimizes allocations because it must be stored on the stack. However, `ref struct` types, in general, can't implement interfaces. This can cause compatibility issues for unit test frameworks and mocking types that use `ref struct` types for implementation. + ## Add more capabilities to the handler -The preceding version of the interpolated string handler implements the pattern. To avoid processing every placeholder expression, you'll need more information in the handler. In this section, you'll improve your handler so that it does less work when the constructed string won't be written to the log. You use to specify a mapping between parameters to a public API and parameters to a handler's constructor. That provides the handler with the information needed to determine if the interpolated string should be evaluated. +The preceding version of the interpolated string handler implements the pattern. To avoid processing every placeholder expression, you need more information in the handler. In this section, you improve your handler so that it does less work when the constructed string isn't written to the log. You use to specify a mapping between parameters to a public API and parameters to a handler's constructor. That provides the handler with the information needed to determine if the interpolated string should be evaluated. Let's start with changes to the Handler. First, add a field to track if the handler is enabled. Add two parameters to the constructor: one to specify the log level for this message, and the other a reference to the log object: @@ -105,13 +107,13 @@ Next, use the field so that your handler only appends literals or formatted obje :::code language="csharp" source="./snippets/interpolated-string-handler/logger-v3.cs" id="AppendWhenEnabled"::: -Next, you'll need to update the `LogMessage` declaration so that the compiler passes the additional parameters to the handler's constructor. That's handled using the on the handler argument: +Next, you need to update the `LogMessage` declaration so that the compiler passes the additional parameters to the handler's constructor. That's handled using the on the handler argument: :::code language="csharp" source="./snippets/interpolated-string-handler/logger-v3.cs" id="ArgumentsToHandlerConstructor"::: This attribute specifies the list of arguments to `LogMessage` that map to the parameters that follow the required `literalLength` and `formattedCount` parameters. The empty string (""), specifies the receiver. The compiler substitutes the value of the `Logger` object represented by `this` for the next argument to the handler's constructor. The compiler substitutes the value of `level` for the following argument. You can provide any number of arguments for any handler you write. The arguments that you add are string arguments. -You can run this version using the same test code. This time, you'll see the following results: +You can run this version using the same test code. This time, you see the following results: ```powershell literal length: 65, formattedCount: 1 @@ -129,9 +131,9 @@ Error Level. CurrentTime: 10/20/2021 12:19:10 PM. This is an error. It will be p Warning Level. This warning is a string, not an interpolated string expression. ``` -You can see that the `AppendLiteral` and `AppendFormat` methods are being called, but they aren't doing any work. The handler has determined that the final string won't be needed, so the handler doesn't build it. There are still a couple of improvements to make. +You can see that the `AppendLiteral` and `AppendFormat` methods are being called, but they aren't doing any work. The handler determined that the final string isn't needed, so the handler doesn't build it. There are still a couple of improvements to make. -First, you can add an overload of `AppendFormatted` that constrains the argument to a type that implements . This overload enables callers to add format strings in the placeholders. While making this change, let's also change the return type of the other `AppendFormatted` and `AppendLiteral` methods, from `void` to `bool` (if any of these methods have different return types, then you'll get a compilation error). That change enables *short circuiting*. The methods return `false` to indicate that processing of the interpolated string expression should be stopped. Returning `true` indicates that it should continue. In this example, you're using it to stop processing when the resulting string isn't needed. Short circuiting supports more fine-grained actions. You could stop processing the expression once it reaches a certain length, to support fixed-length buffers. Or some condition could indicate remaining elements aren't needed. +First, you can add an overload of `AppendFormatted` that constrains the argument to a type that implements . This overload enables callers to add format strings in the placeholders. While making this change, let's also change the return type of the other `AppendFormatted` and `AppendLiteral` methods, from `void` to `bool` (if any of these methods have different return types, then you get a compilation error). That change enables *short circuiting*. The methods return `false` to indicate that processing of the interpolated string expression should be stopped. Returning `true` indicates that it should continue. In this example, you're using it to stop processing when the resulting string isn't needed. Short circuiting supports more fine-grained actions. You could stop processing the expression once it reaches a certain length, to support fixed-length buffers. Or some condition could indicate remaining elements aren't needed. :::code language="csharp" source="./snippets/interpolated-string-handler/logger-v4.cs" id="AppendIFormattable"::: @@ -139,9 +141,9 @@ With that addition, you can specify format strings in your interpolated string e :::code language="csharp" source="./snippets/interpolated-string-handler/Version_4_Examples.cs" id="UseFormattable"::: -The `:t` on the first message specifies the "short time format" for the current time. The previous example showed one of the overloads to the `AppendFormatted` method that you can create for your handler. You don't need to specify a generic argument for the object being formatted. You may have more efficient ways to convert types you create to string. You can write overloads of `AppendFormatted` that takes those types instead of a generic argument. The compiler will pick the best overload. The runtime uses this technique to convert to string output. You can add an integer parameter to specify the *alignment* of the output, with or without an . The that ships with .NET 6 contains nine overloads of for different uses. You can use it as a reference while building a handler for your purposes. +The `:t` on the first message specifies the "short time format" for the current time. The previous example showed one of the overloads to the `AppendFormatted` method that you can create for your handler. You don't need to specify a generic argument for the object being formatted. You might have more efficient ways to convert types you create to string. You can write overloads of `AppendFormatted` that takes those types instead of a generic argument. The compiler picks the best overload. The runtime uses this technique to convert to string output. You can add an integer parameter to specify the *alignment* of the output, with or without an . The that ships with .NET 6 contains nine overloads of for different uses. You can use it as a reference while building a handler for your purposes. -Run the sample now, and you'll see that for the `Trace` message, only the first `AppendLiteral` is called: +Run the sample now, and you see that for the `Trace` message, only the first `AppendLiteral` is called: ```powershell literal length: 60, formattedCount: 1 @@ -170,7 +172,7 @@ You can make one final update to the handler's constructor that improves efficie :::code language="csharp" source="./snippets/interpolated-string-handler/logger-v4.cs" id="UseOutParameter"::: That change means you can remove the `enabled` field. Then, you can change the return type of `AppendLiteral` and `AppendFormatted` to `void`. -Now, when you run the sample, you'll see the following output: +Now, when you run the sample, you see the following output: ```powershell literal length: 60, formattedCount: 1 @@ -195,7 +197,7 @@ Warning Level. This warning is a string, not an interpolated string expression. The only output when `LogLevel.Trace` was specified is the output from the constructor. The handler indicated that it's not enabled, so none of the `Append` methods were invoked. -This example illustrates an important point for interpolated string handlers, especially when logging libraries are used. Any side-effects in the placeholders may not occur. Add the following code to your main program and see this behavior in action: +This example illustrates an important point for interpolated string handlers, especially when logging libraries are used. Any side-effects in the placeholders might not occur. Add the following code to your main program and see this behavior in action: :::code language="csharp" source="./snippets/interpolated-string-handler/Version_4_Examples.cs" id="TestSideEffects"::: @@ -213,4 +215,4 @@ Trace Value of index 15, value of numberOfIncrements: 25 ``` -Interpolated string handlers provide greater control over how an interpolated string expression is converted to a string. The .NET runtime team has already used this feature to improve performance in several areas. You can make use of the same capability in your own libraries. To explore further, look at the . It provides a more complete implementation than you built here. You'll see many more overloads that are possible for the `Append` methods. +Interpolated string handlers provide greater control over how an interpolated string expression is converted to a string. The .NET runtime team used this feature to improve performance in several areas. You can make use of the same capability in your own libraries. To explore further, look at the . It provides a more complete implementation than you built here. You see many more overloads that are possible for the `Append` methods. diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Logger-v1.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Logger-v1.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Logger-v1.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Logger-v1.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Logger-v2.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Logger-v2.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Logger-v2.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Logger-v2.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Program.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Program.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Program.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Program.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_1_Examples.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_1_Examples.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_1_Examples.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_1_Examples.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_2_Examples.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_2_Examples.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_2_Examples.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_2_Examples.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_3_Examples.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_3_Examples.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_3_Examples.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_3_Examples.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_4_Examples.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_4_Examples.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/Version_4_Examples.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/Version_4_Examples.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/interpolated-string-handler.csproj b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/interpolated-string-handler.csproj similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/interpolated-string-handler.csproj rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/interpolated-string-handler.csproj diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/logger-v3.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/logger-v3.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/logger-v3.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/logger-v3.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/logger-v4.cs b/docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/logger-v4.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/interpolated-string-handler/logger-v4.cs rename to docs/csharp/advanced-topics/performance/snippets/interpolated-string-handler/logger-v4.cs diff --git a/docs/csharp/asynchronous-programming/async-return-types.md b/docs/csharp/asynchronous-programming/async-return-types.md index 323051d0a0e7a..684e850ba6251 100644 --- a/docs/csharp/asynchronous-programming/async-return-types.md +++ b/docs/csharp/asynchronous-programming/async-return-types.md @@ -1,7 +1,7 @@ --- title: Async return types description: Learn about the return types that async methods can have in C# with code examples for each type. -ms.date: 02/08/2023 +ms.date: 11/22/2024 --- # Async return types (C#) @@ -19,14 +19,14 @@ For more information about async methods, see [Asynchronous programming with asy Several other types also exist that are specific to Windows workloads: - , for async operations limited to Windows. -- , for async actions in UWP that don't return a value. -- , for async actions in UWP that report progress but don't return a value. -- , for async operations in UWP that return a value. -- , for async operations in UWP that report progress and return a value. +- , for async actions in Universal Windows Platform (UWP) apps that don't return a value. +- , for async actions in UWP apps that report progress but don't return a value. +- , for async operations in UWP apps that return a value. +- , for async operations in UWP apps that report progress and return a value. ## Task return type -Async methods that don't contain a `return` statement or that contain a `return` statement that doesn't return an operand usually have a return type of . Such methods return `void` if they run synchronously. If you use a return type for an async method, a calling method can use an `await` operator to suspend the caller's completion until the called async method has finished. +Async methods that don't contain a `return` statement or that contain a `return` statement that doesn't return an operand usually have a return type of . Such methods return `void` if they run synchronously. If you use a return type for an async method, a calling method can use an `await` operator to suspend the caller's completion until the called async method finishes. In the following example, the `WaitAndApologizeAsync` method doesn't contain a `return` statement, so the method returns a object. Returning a `Task` enables `WaitAndApologizeAsync` to be awaited. The type doesn't include a `Result` property because it has no return value. @@ -48,7 +48,7 @@ In the following example, the `GetLeisureHoursAsync` method contains a `return` :::code language="csharp" source="snippets/async-return-types/async-returns1.cs" ID="LeisureHours"::: -When `GetLeisureHoursAsync` is called from within an await expression in the `ShowTodaysInfo` method, the await expression retrieves the integer value (the value of `leisureHours`) that's stored in the task returned by the `GetLeisureHours` method. For more information about await expressions, see [await](../language-reference/operators/await.md). +When `GetLeisureHoursAsync` is called from within an await expression in the `ShowTodaysInfo` method, the await expression retrieves the integer value (the value of `leisureHours`) stored in the task returned by the `GetLeisureHours` method. For more information about await expressions, see [await](../language-reference/operators/await.md). You can better understand how `await` retrieves the result from a `Task` by separating the call to `GetLeisureHoursAsync` from the application of `await`, as the following code shows. A call to method `GetLeisureHoursAsync` that isn't immediately awaited returns a `Task`, as you would expect from the declaration of the method. The task is assigned to the `getLeisureHoursTask` variable in the example. Because `getLeisureHoursTask` is a , it contains a property of type `TResult`. In this case, `TResult` represents an integer type. When `await` is applied to `getLeisureHoursTask`, the await expression evaluates to the contents of the property of `getLeisureHoursTask`. The value is assigned to the `ret` variable. @@ -73,7 +73,7 @@ The following example shows the behavior of an async event handler. In the examp An async method can return any type that has an accessible `GetAwaiter` method that returns an instance of an *awaiter type*. In addition, the type returned from the `GetAwaiter` method must have the attribute. You can learn more in the article on [Attributes read by the compiler](../language-reference/attributes/general.md#asyncmethodbuilder-attribute) or the C# spec for the [Task type builder pattern](~/_csharpstandard/standard/classes.md#15152-task-type-builder-pattern). -This feature is the complement to [awaitable expressions](~/_csharpstandard/standard/expressions.md#12982-awaitable-expressions), which describes the requirements for the operand of `await`. Generalized async return types enable the compiler to generate `async` methods that return different types. Generalized async return types enabled performance improvements in the .NET libraries. Because and are reference types, memory allocation in performance-critical paths, particularly when allocations occur in tight loops, can adversely affect performance. Support for generalized return types means that you can return a lightweight value type instead of a reference type to avoid additional memory allocations. +This feature is the complement to [awaitable expressions](~/_csharpstandard/standard/expressions.md#12982-awaitable-expressions), which describes the requirements for the operand of `await`. Generalized async return types enable the compiler to generate `async` methods that return different types. Generalized async return types enabled performance improvements in the .NET libraries. Because and are reference types, memory allocation in performance-critical paths, particularly when allocations occur in tight loops, can adversely affect performance. Support for generalized return types means that you can return a lightweight value type instead of a reference type to avoid more memory allocations. .NET provides the structure as a lightweight implementation of a generalized task-returning value. The following example uses the structure to retrieve the value of two dice rolls. @@ -81,11 +81,11 @@ This feature is the complement to [awaitable expressions](~/_csharpstandard/stan Writing a generalized async return type is an advanced scenario, and is targeted for use in specialized environments. Consider using the `Task`, `Task`, and `ValueTask` types instead, which cover most scenarios for asynchronous code. -In C# 10 and later, you can apply the `AsyncMethodBuilder` attribute to an async method (instead of the async return type declaration) to override the builder for that type. Typically you'd apply this attribute to use a different builder provided in the .NET runtime. +You can apply the `AsyncMethodBuilder` attribute to an async method (instead of the async return type declaration) to override the builder for that type. Typically you'd apply this attribute to use a different builder provided in the .NET runtime. ## Async streams with IAsyncEnumerable\ -An async method may return an *async stream*, represented by . An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. The following example shows an async method that generates an async stream: +An async method might return an *async stream*, represented by . An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. The following example shows an async method that generates an async stream: :::code language="csharp" source="snippets/async-return-types/AsyncStreams.cs" ID="GenerateAsyncStream"::: diff --git a/docs/csharp/fundamentals/coding-style/identifier-names.md b/docs/csharp/fundamentals/coding-style/identifier-names.md index 8db1bbeccd02f..740c1d1fd3b98 100644 --- a/docs/csharp/fundamentals/coding-style/identifier-names.md +++ b/docs/csharp/fundamentals/coding-style/identifier-names.md @@ -19,7 +19,7 @@ You can declare identifiers that match C# keywords by using the `@` prefix on th For a complete definition of valid identifiers, see the [Identifiers article in the C# Language Specification](~/_csharpstandard/standard/lexical-structure.md#643-identifiers). > [!IMPORTANT] -> [The C# language specification](~/_csharpstandard/standard/lexical-structure.md#643-identifiers) only allows letter (Lu, Ll, Lt, Lm, Lo or Nl), digit (Nd), connecting (Pc), combining (Mn or Mc), and formatting (Cf) categories. Anything outside that is automatically replaced using `_`. This might impact certain Unicode characters. +> [The C# language specification](~/_csharpstandard/standard/lexical-structure.md#643-identifiers) only allows letter (Lu, Ll, Lt, Lm, or Nl), digit (Nd), connecting (Pc), combining (Mn or Mc), and formatting (Cf) categories. Anything outside that is automatically replaced using `_`. This might impact certain Unicode characters. ## Naming conventions diff --git a/docs/csharp/fundamentals/functional/deconstruct.md b/docs/csharp/fundamentals/functional/deconstruct.md index 2ea5f34ac56a1..c43a9e990c396 100644 --- a/docs/csharp/fundamentals/functional/deconstruct.md +++ b/docs/csharp/fundamentals/functional/deconstruct.md @@ -1,7 +1,7 @@ --- title: Deconstructing tuples and other types description: Learn how to deconstruct tuples and other types. -ms.date: 11/10/2021 +ms.date: 11/22/2024 --- # Deconstructing tuples and other types @@ -35,24 +35,24 @@ There are three ways to deconstruct a tuple: :::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple4.cs" ID="Snippet1"::: - This is cumbersome and isn't recommended. + The preceding example is cumbersome and isn't recommended. -- Lastly, you may deconstruct the tuple into variables that have already been declared. +- Lastly, you can deconstruct the tuple into variables already declared. :::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple5.cs" ID="Snippet1"::: -- Beginning in C# 10, you can mix variable declaration and assignment in a deconstruction. +- You can mix variable declaration and assignment in a deconstruction. :::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple6.cs" ID="Snippet1"::: You can't specify a specific type outside the parentheses even if every field in the tuple has the -same type. Doing so generates compiler error CS8136, "Deconstruction 'var (...)' form disallows a specific type for 'var'.". +same type. Doing so generates compiler error CS8136, "Deconstruction 'var (...)' form disallows a specific type for 'var'." You must assign each element of the tuple to a variable. If you omit any elements, the compiler generates error CS8132, "Can't deconstruct a tuple of 'x' elements into 'y' variables." ## Tuple elements with discards -Often when deconstructing a tuple, you're interested in the values of only some elements. You can take advantage of C#'s support for *discards*, which are write-only variables whose values you've chosen to ignore. A discard is chosen by an underscore character ("\_") in an assignment. You can discard as many values as you like; all are represented by the single discard, `_`. +Often when deconstructing a tuple, you're interested in the values of only some elements. You can take advantage of C#'s support for *discards*, which are write-only variables whose values you chose to ignore. You declare a discard with an underscore character ("\_") in an assignment. You can discard as many values as you like; a single discard, `_`, represents all the discarded values. The following example illustrates the use of tuples with discards. The `QueryCityDataForYears` method returns a six-tuple with the name of a city, its area, a year, the city's population for that year, a second year, and the city's population for that second year. The example shows the change in population between those two years. Of the data available from the tuple, we're unconcerned with the city area, and we know the city name and the two dates at design-time. As a result, we're only interested in the two population values stored in the tuple, and can handle its remaining values as discards. @@ -60,7 +60,7 @@ The following example illustrates the use of tuples with discards. The `QueryCit ## User-defined types -C# doesn't offer built-in support for deconstructing non-tuple types other than the [`record`](#record-types) and [DictionaryEntry](xref:System.Collections.DictionaryEntry.Deconstruct%2A) types. However, as the author of a class, a struct, or an interface, you can allow instances of the type to be deconstructed by implementing one or more `Deconstruct` methods. The method returns void, and each value to be deconstructed is indicated by an [out](../../language-reference/keywords/method-parameters.md#out-parameter-modifier) parameter in the method signature. For example, the following `Deconstruct` method of a `Person` class returns the first, middle, and last name: +C# offers built-in support for deconstructing tuple types, [`record`](#record-types), and [DictionaryEntry](xref:System.Collections.DictionaryEntry.Deconstruct%2A) types. However, as the author of a class, a struct, or an interface, you can allow instances of the type to be deconstructed by implementing one or more `Deconstruct` methods. The method returns void. An [out](../../language-reference/keywords/method-parameters.md#out-parameter-modifier) parameter in the method signature represents each value to be deconstructed. For example, the following `Deconstruct` method of a `Person` class returns the first, middle, and family name: :::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-class1.cs" ID="Snippet1"::: @@ -70,44 +70,36 @@ You can then deconstruct an instance of the `Person` class named `p` with an ass The following example overloads the `Deconstruct` method to return various combinations of properties of a `Person` object. Individual overloads return: -- A first and last name. -- A first, middle, and last name. -- A first name, a last name, a city name, and a state name. +- A first and family name. +- A first, middle, and family name. +- A first name, a family name, a city name, and a state name. :::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-class2.cs"::: -Multiple `Deconstruct` methods having the same number of parameters are ambiguous. You must be careful to define `Deconstruct` methods with different numbers of parameters, or "arity". `Deconstruct` methods with the same number of parameters cannot be distinguished during overload resolution. +Multiple `Deconstruct` methods having the same number of parameters are ambiguous. You must be careful to define `Deconstruct` methods with different numbers of parameters, or "arity". `Deconstruct` methods with the same number of parameters can't be distinguished during overload resolution. ## User-defined type with discards -Just as you do with [tuples](#tuple-elements-with-discards), you can use discards to ignore selected items returned by a `Deconstruct` method. Each discard is defined by a variable named "\_", and a single deconstruction operation can include multiple discards. +Just as you do with [tuples](#tuple-elements-with-discards), you can use discards to ignore selected items returned by a `Deconstruct` method. A variable named "\_" represents a discard. A single deconstruction operation can include multiple discards. -The following example deconstructs a `Person` object into four strings (the first and last names, the city, and the state) but discards the last name and the state. +The following example deconstructs a `Person` object into four strings (the first and family names, the city, and the state) but discards the family name and the state. :::code language="csharp" source="./snippets/deconstructing-tuples/class-discard1.cs" ID="Snippet1"::: -## Extension methods for user-defined types +## Deconstruction extension methods If you didn't author a class, struct, or interface, you can still deconstruct objects of that type by implementing one or more `Deconstruct` [extension methods](../../programming-guide/classes-and-structs/extension-methods.md) to return the values in which you're interested. -The following example defines two `Deconstruct` extension methods for the class. The first returns a set of values that indicate the characteristics of the property, including its type, whether it's static or instance, whether it's read-only, and whether it's indexed. The second indicates the property's accessibility. Because the accessibility of get and set accessors can differ, Boolean values indicate whether the property has separate get and set accessors and, if it does, whether they have the same accessibility. If there's only one accessor or both the get and the set accessor have the same accessibility, the `access` variable indicates the accessibility of the property as a whole. Otherwise, the accessibility of the get and set accessors are indicated by the `getAccess` and `setAccess` variables. +The following example defines two `Deconstruct` extension methods for the class. The first returns a set of values that indicate the characteristics of the property. The second indicates the property's accessibility. Boolean values indicate whether the property has separate get and set accessors or different accessibility. If there's only one accessor or both the get and the set accessor have the same accessibility, the `access` variable indicates the accessibility of the property as a whole. Otherwise, the accessibility of the get and set accessors are indicated by the `getAccess` and `setAccess` variables. :::code source="./snippets/deconstructing-tuples/deconstruct-extension1.cs"::: ## Extension method for system types -Some system types provide the `Deconstruct` method as a convenience. For example, the type provides this functionality. When you're iterating over a each element is a `KeyValuePair` and can be deconstructed. Consider the following example: +Some system types provide the `Deconstruct` method as a convenience. For example, the type provides this functionality. When you're iterating over a , each element is a `KeyValuePair` and can be deconstructed. Consider the following example: :::code source="./snippets/deconstructing-tuples/deconstruct-kvp.cs" id="KeyValuePair"::: -You can add a `Deconstruct` method to system types that don't have one. Consider the following extension method: - -:::code source="./snippets/deconstructing-tuples/deconstruct-extension2.cs" id="NullableExtensions"::: - -This extension method allows all types to be deconstructed into a tuple of `(bool hasValue, T value)`. The following example shows code that uses this extension method: - -:::code source="./snippets/deconstructing-tuples/deconstruct-extension2.cs" id="NullableExample"::: - ## `record` types When you declare a [record](../../language-reference/builtin-types/record.md) type by using two or more positional parameters, the compiler creates a `Deconstruct` method with an `out` parameter for each positional parameter in the `record` declaration. For more information, see [Positional syntax for property definition](../../language-reference/builtin-types/record.md#positional-syntax-for-property-definition) and [Deconstructor behavior in derived records](../../language-reference/builtin-types/record.md#deconstructor-behavior-in-derived-records). diff --git a/docs/csharp/fundamentals/functional/pattern-matching.md b/docs/csharp/fundamentals/functional/pattern-matching.md index 4a095c54d5c9f..a3b24fc943169 100644 --- a/docs/csharp/fundamentals/functional/pattern-matching.md +++ b/docs/csharp/fundamentals/functional/pattern-matching.md @@ -110,6 +110,6 @@ This article provided a tour of the kinds of code you can write with pattern mat ## See also - [Use pattern matching to avoid 'is' check followed by a cast (style rules IDE0020 and IDE0038)](../../../fundamentals/code-analysis/style-rules/ide0020-ide0038.md) -- [Exploration: Use pattern matching to build your class behavior for better code](../../whats-new/tutorials/patterns-objects.md) +- [Exploration: Use pattern matching to build your class behavior for better code](../../tutorials/patterns-objects.md) - [Tutorial: Use pattern matching to build type-driven and data-driven algorithms](../tutorials/pattern-matching.md) - [Reference: Pattern matching](../../language-reference/operators/patterns.md) diff --git a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/Program.cs b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/Program.cs index a92d8ce7f93a3..72f859addc661 100644 --- a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/Program.cs +++ b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/Program.cs @@ -13,7 +13,6 @@ static void Main(string[] args) ExampleDiscard.Main(); ExampleClassDeconstruction.Main(); ExampleExtension.Main(); - ExampleNullableEx.Main(); ExampleSystem.KeyValuePair(); } } diff --git a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension2.cs b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension2.cs deleted file mode 100644 index a4fb2341b069f..0000000000000 --- a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension2.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -public class ExampleNullableEx -{ - public static void Main() - { - // - DateTime? questionableDateTime = default; - var (hasValue, value) = questionableDateTime; - Console.WriteLine( - $"{{ HasValue = {hasValue}, Value = {value} }}"); - - questionableDateTime = DateTime.Now; - (hasValue, value) = questionableDateTime; - Console.WriteLine( - $"{{ HasValue = {hasValue}, Value = {value} }}"); - - // Example outputs: - // { HasValue = False, Value = 1/1/0001 12:00:00 AM } - // { HasValue = True, Value = 11/10/2021 6:11:45 PM } - // - } -} - - -// -public static class NullableExtensions -{ - public static void Deconstruct( - this T? nullable, - out bool hasValue, - out T value) where T : struct - { - hasValue = nullable.HasValue; - value = nullable.GetValueOrDefault(); - } -} -// diff --git a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj index 0f6d1aa50ca87..2de6b53d97405 100644 --- a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj +++ b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable deconstruction.Program diff --git a/docs/csharp/fundamentals/program-structure/top-level-statements.md b/docs/csharp/fundamentals/program-structure/top-level-statements.md index 2484af729de86..fdf8239d96948 100644 --- a/docs/csharp/fundamentals/program-structure/top-level-statements.md +++ b/docs/csharp/fundamentals/program-structure/top-level-statements.md @@ -1,7 +1,7 @@ --- title: "Top-level statements - programs without Main methods" description: Learn about top-level statements. You can create programs without the ceremony of a Program class and a Main method. -ms.date: 08/19/2021 +ms.date: 11/22/2024 helpviewer_keywords: - "C# language, top-level statements" - "C# language, Main method" @@ -10,11 +10,11 @@ helpviewer_keywords: You don't have to explicitly include a `Main` method in a console application project. Instead, you can use the *top-level statements* feature to minimize the code you have to write. -Top-level statements allows you to write executable code directly at the root of a file, eliminating the need for wrapping your code in a class or method. +Top-level statements allow you to write executable code directly at the root of a file, eliminating the need for wrapping your code in a class or method. This means you can create programs without the ceremony of a `Program` class and a `Main` method. In this case, the compiler generates a `Program` class with an entry point method for the application. The name of the generated method isn't `Main`, it's an implementation detail that your code can't reference directly. -Here's a *Program.cs* file that is a complete C# program in C# 10: +Here's a *Program.cs* file that is a complete C# program: ```csharp Console.WriteLine("Hello World!"); @@ -30,7 +30,7 @@ An application must have only one entry point. A project can have only one file > CS8802 Only one compilation unit can have top-level statements. -A project can have any number of additional source code files that don't have top-level statements. +A project can have any number of source code files that don't have top-level statements. ## No other entry points diff --git a/docs/csharp/fundamentals/types/namespaces.md b/docs/csharp/fundamentals/types/namespaces.md index 5602a36c33999..0562f4b721d66 100644 --- a/docs/csharp/fundamentals/types/namespaces.md +++ b/docs/csharp/fundamentals/types/namespaces.md @@ -1,7 +1,7 @@ --- title: "Organizing types in namespaces" description: Learn how namespaces help you organize related types. -ms.date: 05/14/2021 +ms.date: 11/24/2024 helpviewer_keywords: - "C# language, namespaces" - "namespaces [C#]" @@ -28,7 +28,7 @@ Second, declaring your own namespaces can help you control the scope of class an The name of the namespace must be a valid C# [identifier name](../coding-style/identifier-names.md). -Beginning with C# 10, you can declare a namespace for all types defined in that file, as shown in the following example: +You can declare a namespace for all types defined in that file, as shown in the following example: :::code language="csharp" source="snippets/namespaces/filescopednamespace.cs"::: @@ -41,7 +41,7 @@ Namespaces have the following properties: - They organize large code projects. - They're delimited by using the `.` operator. - The `using` directive obviates the requirement to specify the name of the namespace for every class. -- The `global` namespace is the "root" namespace: `global::System` will always refer to the .NET namespace. +- The `global` namespace is the "root" namespace: `global::System` always refers to the .NET namespace. ## C# language specification diff --git a/docs/csharp/how-to/concatenate-multiple-strings.md b/docs/csharp/how-to/concatenate-multiple-strings.md index c89c5984eacc3..c41f2a6a9c6b6 100644 --- a/docs/csharp/how-to/concatenate-multiple-strings.md +++ b/docs/csharp/how-to/concatenate-multiple-strings.md @@ -1,12 +1,11 @@ --- title: "How to concatenate multiple strings" description: There are multiple ways to concatenate strings in C#. Learn the options and the reasons behind different choices. -ms.date: 03/15/2024 +ms.date: 11/22/2024 helpviewer_keywords: - "joining strings [C#]" - "concatenating strings [C#]" - "strings [C#], concatenation" -ms.assetid: 8e16736f-4096-4f3f-be0f-9d4c3ff63520 --- # How to concatenate multiple strings (C# Guide) @@ -35,7 +34,7 @@ In some expressions, it's easier to concatenate strings using string interpolati > [!NOTE] > In string concatenation operations, the C# compiler treats a null string the same as an empty string. -Beginning with C# 10, you can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. +You can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. ## `String.Format` @@ -43,7 +42,7 @@ Another method to concatenate strings is class was designed for these scenarios. The following code uses the method of the class to concatenate strings. +In other cases, you might be combining strings in a loop where you don't know how many source strings you're combining, and the actual number of source strings can be large. The class was designed for these scenarios. The following code uses the method of the class to concatenate strings. :::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet4"::: diff --git a/docs/csharp/language-reference/attributes/general.md b/docs/csharp/language-reference/attributes/general.md index 0588558460c82..443fdda24d07a 100644 --- a/docs/csharp/language-reference/attributes/general.md +++ b/docs/csharp/language-reference/attributes/general.md @@ -1,6 +1,6 @@ --- title: "Attributes interpreted by the compiler: Miscellaneous" -ms.date: 07/26/2024 +ms.date: 11/22/2024 description: "Learn about attributes that affect code generated by the compiler: the Conditional, Obsolete, AttributeUsage, ModuleInitializer, and SkipLocalsInit attributes." --- # Miscellaneous attributes interpreted by the C# compiler @@ -54,9 +54,7 @@ In the following example, the `Obsolete` attribute is applied to class `A` and t :::code language="csharp" source="snippets/ObsoleteExample.cs" ID="Snippet1" interactive="try-dotnet" ::: -The string provided as the first argument to the attribute constructor is displayed as part of the warning or error. Two warnings for class `A` are generated: one for the declaration of the class reference, and one for the class constructor. The `Obsolete` attribute can be used without arguments, but including an explanation what to use instead is recommended. - -In C# 10, you can use constant string interpolation and the `nameof` operator to ensure the names match: +The string provided as the first argument to the attribute constructor is displayed as part of the warning or error. Two warnings for class `A` are generated: one for the declaration of the class reference, and one for the class constructor. The `Obsolete` attribute can be used without arguments, but including an explanation what to use instead is recommended. You can use constant string interpolation and the `nameof` operator to ensure the names match: :::code language="csharp" source="snippets/ObsoleteExample.cs" id="Snippet2" ::: @@ -165,13 +163,13 @@ You can learn about async method builders by reading about the following builder - - -In C# 10 and later, the `AsyncMethodBuilder` attribute can be applied to an async method to override the builder for that type. +The `AsyncMethodBuilder` attribute can be applied to an async method to override the builder for that type. ## `InterpolatedStringHandler` and `InterpolatedStringHandlerArguments` attributes -Starting with C# 10, you use these attributes to specify that a type is an *interpolated string handler*. The .NET 6 library already includes for scenarios where you use an interpolated string as the argument for a `string` parameter. You might have other instances where you want to control how interpolated strings are processed. You apply the to the type that implements your handler. You apply the to parameters of that type's constructor. +You use these attributes to specify that a type is an *interpolated string handler*. The .NET 6 library already includes for scenarios where you use an interpolated string as the argument for a `string` parameter. You might have other instances where you want to control how interpolated strings are processed. You apply the to the type that implements your handler. You apply the to parameters of that type's constructor. -You can learn more about building an interpolated string handler in the C# 10 feature specification for [interpolated string improvements](~/_csharplang/proposals/csharp-10.0/improved-interpolated-strings.md). +You can learn more about building an interpolated string handler in the feature specification for [interpolated string improvements](~/_csharplang/proposals/csharp-10.0/improved-interpolated-strings.md). ## `ModuleInitializer` attribute diff --git a/docs/csharp/language-reference/builtin-types/default-values.md b/docs/csharp/language-reference/builtin-types/default-values.md index 2f3095ed8b373..4771649cda094 100644 --- a/docs/csharp/language-reference/builtin-types/default-values.md +++ b/docs/csharp/language-reference/builtin-types/default-values.md @@ -1,7 +1,7 @@ --- -title: "Default values of built in types" -description: "Learn the default values of C# types such as bool, char, int, float, double and more." -ms.date: 09/28/2021 +title: "Default values of built-in types" +description: "Learn the default values of C# types such as bool, char, int, float, double, and more." +ms.date: 11/22/2024 helpviewer_keywords: - "default [C#]" - "parameterless constructor [C#]" @@ -47,7 +47,7 @@ Console.WriteLine(n); // output: (0, 0) At run time, if the instance represents a value type, you can use the method to invoke the parameterless constructor to obtain the default value of the type. > [!NOTE] -> In C# 10 and later, a [structure type](struct.md) (which is a value type) may have an [explicit parameterless constructor](struct.md#struct-initialization-and-default-values) that may produce a non-default value of the type. Thus, we recommend using the `default` operator or the `default` literal to produce the default value of a type. +> A [structure type](struct.md) (which is a value type) may have an [explicit parameterless constructor](struct.md#struct-initialization-and-default-values) that may produce a non-default value of the type. Thus, we recommend using the `default` operator or the `default` literal to produce the default value of a type. ## C# language specification @@ -55,7 +55,7 @@ For more information, see the following sections of the [C# language specificati - [Default values](~/_csharpstandard/standard/variables.md#93-default-values) - [Default constructors](~/_csharpstandard/standard/types.md#833-default-constructors) -- [C# 10 - Parameterless struct constructors](~/_csharplang/proposals/csharp-10.0/parameterless-struct-constructors.md) +- [Parameterless struct constructors](~/_csharplang/proposals/csharp-10.0/parameterless-struct-constructors.md) - [C# 11 - Auto default structs](~/_csharplang/proposals/csharp-11.0/auto-default-structs.md) ## See also diff --git a/docs/csharp/language-reference/builtin-types/nullable-reference-types.md b/docs/csharp/language-reference/builtin-types/nullable-reference-types.md index d5f28532df213..6f81d1f703e80 100644 --- a/docs/csharp/language-reference/builtin-types/nullable-reference-types.md +++ b/docs/csharp/language-reference/builtin-types/nullable-reference-types.md @@ -1,25 +1,25 @@ --- title: "Nullable reference types" description: Learn about C# nullable reference types and how to use them -ms.date: 09/08/2021 +ms.date: 11/22/2024 --- # Nullable reference types (C# reference) > [!NOTE] > This article covers nullable reference types. You can also declare [nullable value types](nullable-value-types.md). -Nullable reference types are available in code that has opted in to a *nullable aware context*. Nullable reference types, the null static analysis warnings, and the [null-forgiving operator](../operators/null-forgiving.md) are optional language features. All are turned off by default. A *nullable context* is controlled at the project level using build settings, or in code using pragmas. +Nullable reference types are available in code that's in a *nullable aware context*. Nullable reference types, the null static analysis warnings, and the [null-forgiving operator](../operators/null-forgiving.md) are optional language features. All are turned off by default. A *nullable context* is controlled at the project level using build settings, or in code using pragmas. > [!IMPORTANT] -> All project templates starting with .NET 6 (C# 10) enable the *nullable context* for the project. Projects created with earlier templates don't include this element, and these features are off unless you enable them in the project file or use pragmas. +> All project templates enable the *nullable context* for the project. Projects created with earlier templates don't include this element, and these features are off unless you enable them in the project file or use pragmas. In a nullable aware context: -- A variable of a reference type `T` must be initialized with non-null, and may never be assigned a value that may be `null`. -- A variable of a reference type `T?` may be initialized with `null` or assigned `null`, but is required to be checked against `null` before de-referencing. +- A variable of a reference type `T` must be initialized with non-null, and can never be assigned a value that might be `null`. +- A variable of a reference type `T?` can be initialized with `null` or assigned `null`, but is required to be checked against `null` before dereferencing. - A variable `m` of type `T?` is considered to be non-null when you apply the null-forgiving operator, as in `m!`. -The distinctions between a non-nullable reference type `T` and a nullable reference type `T?` are enforced by the compiler's interpretation of the preceding rules. A variable of type `T` and a variable of type `T?` are represented by the same .NET type. The following example declares a non-nullable string and a nullable string, and then uses the null-forgiving operator to assign a value to a non-nullable string: +The compiler enforces the distinctions between a non-nullable reference type `T` and a nullable reference type `T?` using the preceding rules. A variable of type `T` and a variable of type `T?` are the same .NET type. The following example declares a non-nullable string and a nullable string, and then uses the null-forgiving operator to assign a value to a non-nullable string: :::code language="csharp" source="snippets/shared/NullableReferenceTypes.cs" id="SnippetCoreSyntax"::: @@ -53,7 +53,7 @@ Non-nullable reference types should always be safe to dereference because their > [!NOTE] > If you assign a *maybe-null* expression to a non-nullable reference type, the compiler generates a warning. The compiler then generates warnings for that variable until it's assigned to a *not-null* expression. -Nullable reference types may be initialized or assigned to `null`. Therefore, static analysis must determine that a variable is *not-null* before it's dereferenced. If a nullable reference is determined to be *maybe-null*, assigning to a non-nullable reference variable generates a compiler warning. The following class shows examples of these warnings: +Nullable reference types can be initialized or assigned to `null`. Therefore, static analysis must determine that a variable is *not-null* before it's dereferenced. If a nullable reference is determined to be *maybe-null*, assigning to a non-nullable reference variable generates a compiler warning. The following class shows examples of these warnings: :::code language="csharp" source="snippets/shared/NullableReferenceTypes.cs" id="SnippetClassWithNullable"::: @@ -61,11 +61,11 @@ The following snippet shows where the compiler emits warnings when using this cl :::code language="csharp" source="snippets/shared/NullableReferenceTypes.cs" id="SnippetLocalWarnings"::: -The preceding examples demonstrate how compiler's static analysis determines the *null-state* of reference variables. The compiler applies language rules for null checks and assignments to inform its analysis. The compiler can't make assumptions about the semantics of methods or properties. If you call methods that perform null checks, the compiler can't know those methods affect a variable's *null-state*. There are attributes you can add to your APIs to inform the compiler about the semantics of arguments and return values. These attributes have been applied to many common APIs in the .NET Core libraries. For example, has been updated, and the compiler correctly interprets that method as a null check. For more information about the attributes that apply to *null-state* static analysis, see the article on [Nullable attributes](../attributes/nullable-analysis.md). +The preceding examples demonstrate how compiler's static analysis determines the *null-state* of reference variables. The compiler applies language rules for null checks and assignments to inform its analysis. The compiler can't make assumptions about the semantics of methods or properties. If you call methods that perform null checks, the compiler can't know those methods affect a variable's *null-state*. There are attributes you can add to your APIs to inform the compiler about the semantics of arguments and return values. Many common APIs in the .NET libraries have these attributes. For example, the compiler correctly interprets as a null check. For more information about the attributes that apply to *null-state* static analysis, see the article on [Nullable attributes](../attributes/nullable-analysis.md). ## Setting the nullable context -There are two ways to control the nullable context. At the project level, you can add the `enable` project setting. In a single C# source file, you can add the `#nullable enable` pragma to enable the nullable context. See the article on [setting a nullable strategy](../../nullable-migration-strategies.md). Prior to .NET 6, new projects use the default, `disable`. Beginning with .NET 6, new projects include the `enable` element in the project file. +There are two ways to control the nullable context. At the project level, you can add the `enable` project setting. In a single C# source file, you can add the `#nullable enable` pragma to enable the nullable context. See the article on [setting a nullable strategy](../../nullable-migration-strategies.md). Before .NET 6, new projects use the default, `disable`. Beginning with .NET 6, new projects include the `enable` element in the project file. ## C# language specification diff --git a/docs/csharp/language-reference/builtin-types/record.md b/docs/csharp/language-reference/builtin-types/record.md index 9d4bf638a58c3..4c395209b61eb 100644 --- a/docs/csharp/language-reference/builtin-types/record.md +++ b/docs/csharp/language-reference/builtin-types/record.md @@ -1,7 +1,7 @@ --- title: "Records" description: Learn about the record type in C# -ms.date: 05/25/2023 +ms.date: 11/22/2023 f1_keywords: - "record_CSharpKeyword" helpviewer_keywords: @@ -10,7 +10,7 @@ helpviewer_keywords: --- # Records (C# reference) -You use the `record` modifier to define a [reference type](reference-types.md) that provides built-in functionality for encapsulating data. C# 10 allows the `record class` syntax as a synonym to clarify a reference type, and `record struct` to define a [value type](value-types.md) with similar functionality. +You use the `record` modifier to define a [reference type](reference-types.md) that provides built-in functionality for encapsulating data. The `record class` syntax as a synonym to clarify a reference type, and `record struct` to define a [value type](value-types.md) with similar functionality. When you declare a [primary constructor](../../programming-guide/classes-and-structs/instance-constructors.md#primary-constructors) on a record, the compiler generates public properties for the primary constructor parameters. The primary constructor parameters to a record are referred to as *positional parameters*. The compiler creates *positional properties* that mirror the primary constructor or positional parameters. The compiler doesn't synthesize properties for primary constructor parameters on types that don't have the `record` modifier. @@ -47,7 +47,7 @@ The preceding examples show some distinctions between records that are reference - A `record` or a `record class` declares a reference type. The `class` keyword is optional, but can add clarity for readers. A `record struct` declares a value type. - Positional properties are *immutable* in a `record class` and a `readonly record struct`. They're *mutable* in a `record struct`. -The remainder of this article discusses both `record class` and `record struct` types. The differences are detailed in each section. You should decide between a `record class` and a `record struct` similar to deciding between a `class` and a `struct`. The term *record* is used to describe behavior that applies to all record types. Either `record struct` or `record class` is used to describe behavior that applies to only struct or class types, respectively. The `record struct` type was introduced in C# 10. +The remainder of this article discusses both `record class` and `record struct` types. The differences are detailed in each section. You should decide between a `record class` and a `record struct` similar to deciding between a `class` and a `struct`. The term *record* is used to describe behavior that applies to all record types. Either `record struct` or `record class` is used to describe behavior that applies to only struct or class types, respectively. ## Positional syntax for property definition @@ -64,13 +64,13 @@ When you use the positional syntax for property definition, the compiler creates * For record struct types, a parameterless constructor that sets each field to its default value. * A `Deconstruct` method with an `out` parameter for each positional parameter provided in the record declaration. The method deconstructs properties defined by using positional syntax; it ignores properties that are defined by using standard property syntax. -You may want to add attributes to any of these elements the compiler creates from the record definition. You can add a *target* to any attribute you apply to the positional record's properties. The following example applies the to each property of the `Person` record. The `property:` target indicates that the attribute is applied to the compiler-generated property. Other values are `field:` to apply the attribute to the field, and `param:` to apply the attribute to the parameter. +You might want to add attributes to any of these elements the compiler creates from the record definition. You can add a *target* to any attribute you apply to the positional record's properties. The following example applies the to each property of the `Person` record. The `property:` target indicates that the attribute is applied to the compiler-generated property. Other values are `field:` to apply the attribute to the field, and `param:` to apply the attribute to the parameter. :::code language="csharp" source="snippets/shared/RecordType.cs" id="PositionalAttributes"::: The preceding example also shows how to create XML documentation comments for the record. You can add the `` tag to add documentation for the primary constructor's parameters. -If the generated automatically implemented property definition isn't what you want, you can define your own property of the same name. For example, you may want to change accessibility or mutability, or provide an implementation for either the `get` or `set` accessor. If you declare the property in your source, you must initialize it from the positional parameter of the record. If your property is an automatically implemented property, you must initialize the property. If you add a backing field in your source, you must initialize the backing field. The generated deconstructor uses your property definition. For instance, the following example declares the `FirstName` and `LastName` properties of a positional record `public`, but restricts the `Id` positional parameter to `internal`. You can use this syntax for records and record struct types. +If the generated automatically implemented property definition isn't what you want, you can define your own property of the same name. For example, you might want to change accessibility or mutability, or provide an implementation for either the `get` or `set` accessor. If you declare the property in your source, you must initialize it from the positional parameter of the record. If your property is an automatically implemented property, you must initialize the property. If you add a backing field in your source, you must initialize the backing field. The generated deconstructor uses your property definition. For instance, the following example declares the `FirstName` and `LastName` properties of a positional record `public`, but restricts the `Id` positional parameter to `internal`. You can use this syntax for records and record struct types. :::code language="csharp" source="snippets/shared/RecordType.cs" id="PositionalWithManualProperty"::: @@ -159,7 +159,7 @@ The `ToString` override creates a object with t :::code language="csharp" source="snippets/shared/RecordType.cs" id="ToStringOverrideDefault"::: -You can provide your own implementation of `PrintMembers` or the `ToString` override. Examples are provided in the [`PrintMembers` formatting in derived records](#printmembers-formatting-in-derived-records) section later in this article. In C# 10 and later, your implementation of `ToString` may include the `sealed` modifier, which prevents the compiler from synthesizing a `ToString` implementation for any derived records. You can create a consistent string representation throughout a hierarchy of `record` types. (Derived records still have a `PrintMembers` method generated for all derived properties.) +You can provide your own implementation of `PrintMembers` or the `ToString` override. Examples are provided in the [`PrintMembers` formatting in derived records](#printmembers-formatting-in-derived-records) section later in this article. Your implementation of `ToString` might include the `sealed` modifier, which prevents the compiler from synthesizing a `ToString` implementation for any derived records. You can create a consistent string representation throughout a hierarchy of `record` types. (Derived records still have a `PrintMembers` method generated for all derived properties.) ## Inheritance @@ -211,7 +211,7 @@ Here's an example of code that replaces the synthesized `PrintMembers` methods, :::code language="csharp" source="snippets/shared/RecordType.cs" id="PrintMembersImplementation"::: > [!NOTE] -> In C# 10 and later, the compiler will synthesize `PrintMembers` in derived records even when a base record has sealed the `ToString` method. You can also create your own implementation of `PrintMembers`. +> The compiler will synthesize `PrintMembers` in derived records even when a base record has sealed the `ToString` method. You can also create your own implementation of `PrintMembers`. ### Deconstructor behavior in derived records diff --git a/docs/csharp/language-reference/builtin-types/struct.md b/docs/csharp/language-reference/builtin-types/struct.md index 5fecf3081b714..7ce46f3592ded 100644 --- a/docs/csharp/language-reference/builtin-types/struct.md +++ b/docs/csharp/language-reference/builtin-types/struct.md @@ -1,7 +1,7 @@ --- title: "Structure types" description: Learn about the struct type in C# -ms.date: 04/05/2023 +ms.date: 11/22/2024 f1_keywords: - "struct_CSharpKeyword" helpviewer_keywords: @@ -13,7 +13,7 @@ helpviewer_keywords: A *structure type* (or *struct type*) is a [value type](value-types.md) that can encapsulate data and related functionality. You use the `struct` keyword to define a structure type: -[!code-csharp[struct example](snippets/shared/StructType.cs#StructExample)] +:::code language="csharp" source="snippets/shared/StructType.cs" id="StructExample"::: For information about `ref struct` and `readonly ref struct` types, see the [ref structure types](ref-struct.md) article. @@ -28,7 +28,7 @@ Because structure types have value semantics, we recommend you define *immutable You use the `readonly` modifier to declare that a structure type is immutable. All data members of a `readonly` struct must be read-only as follows: - Any field declaration must have the [`readonly` modifier](../keywords/readonly.md) -- Any property, including automatically implemented ones, must be read-only or [`init` only](../keywords/init.md). Note that init-only setters are only available from [C# version 9 onwards](../../whats-new/csharp-version-history.md). +- Any property, including automatically implemented ones, must be read-only or [`init` only](../keywords/init.md). Init-only setters are only available from [C# version 9 onwards](../../whats-new/csharp-version-history.md). That guarantees that no member of a `readonly` struct modifies the state of the struct. That means that other instance members except constructors are implicitly [`readonly`](#readonly-instance-members). @@ -37,7 +37,7 @@ That guarantees that no member of a `readonly` struct modifies the state of the The following code defines a `readonly` struct with init-only property setters: -[!code-csharp[readonly struct](snippets/shared/StructType.cs#ReadonlyStruct)] +:::code language="csharp" source="snippets/shared/StructType.cs" id="ReadonlyStruct"::: ## `readonly` instance members @@ -49,38 +49,38 @@ Typically, you apply the `readonly` modifier to the following kinds of instance - methods: - [!code-csharp[readonly method](snippets/shared/StructType.cs#ReadonlyMethod)] + :::code language="csharp" source="snippets/shared/StructType.cs" id="ReadonlyMethod"::: You can also apply the `readonly` modifier to methods that override methods declared in : - [!code-csharp[readonly override](snippets/shared/StructType.cs#ReadonlyOverride)] + :::code language="csharp" source="snippets/shared/StructType.cs" id="ReadonlyOverride"::: - properties and indexers: - [!code-csharp[readonly property get](snippets/shared/StructType.cs#ReadonlyProperty)] + :::code language="csharp" source="snippets/shared/StructType.cs" id="ReadonlyProperty"::: If you need to apply the `readonly` modifier to both accessors of a property or indexer, apply it in the declaration of the property or indexer. > [!NOTE] > The compiler declares a `get` accessor of an [automatically implemented property](../../programming-guide/classes-and-structs/auto-implemented-properties.md) as `readonly`, regardless of presence of the `readonly` modifier in a property declaration. - You may apply the `readonly` modifier to a property or indexer with an `init` accessor: + You can apply the `readonly` modifier to a property or indexer with an `init` accessor: :::code language="csharp" source="snippets/shared/StructType.cs" id="ReadonlyWithInit"::: You can apply the `readonly` modifier to static fields of a structure type, but not any other static members, such as properties or methods. -The compiler may make use of the `readonly` modifier for performance optimizations. For more information, see [Avoiding allocations](../../advanced-topics/performance/index.md). +The compiler can make use of the `readonly` modifier for performance optimizations. For more information, see [Avoiding allocations](../../advanced-topics/performance/index.md). ## Nondestructive mutation -Beginning with C# 10, you can use the [`with` expression](../operators/with-expression.md) to produce a copy of a structure-type instance with the specified properties and fields modified. You use [object initializer](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) syntax to specify what members to modify and their new values, as the following example shows: +You can use the [`with` expression](../operators/with-expression.md) to produce a copy of a structure-type instance with the specified properties and fields modified. You use [object initializer](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) syntax to specify what members to modify and their new values, as the following example shows: :::code language="csharp" source="snippets/shared/StructType.cs" id="WithExpression"::: ## `record` struct -Beginning with C# 10, you can define record structure types. Record types provide built-in functionality for encapsulating data. You can define both `record struct` and `readonly record struct` types. A record struct can't be a [`ref struct`](ref-struct.md). For more information and examples, see [Records](record.md). +You can define record structure types. Record types provide built-in functionality for encapsulating data. You can define both `record struct` and `readonly record struct` types. A record struct can't be a [`ref struct`](ref-struct.md). For more information and examples, see [Records](record.md). ## Inline arrays @@ -104,7 +104,7 @@ There are minimal restrictions on the type of the single field of an inline arra :::code language="csharp" source="snippets/shared/StructType.cs" id="DeclareInlineArrayWithPointer"::: -but it can be any reference type, or any value type: +But it can be any reference type, or any value type: :::code language="csharp" source="snippets/shared/StructType.cs" id="DeclareInlineArrayWithReferenceType"::: @@ -127,38 +127,38 @@ The most common situation where you see default values is in arrays or in other TemperatureRange[] lastMonth = new TemperatureRange[30]; ``` -All of a struct's member fields must be *definitely assigned* when it's created because `struct` types directly store their data. The `default` value of a struct has *definitely assigned* all fields to 0. All fields must be definitely assigned when a constructor is invoked. You initialize fields using the following mechanisms: +All of a struct's member fields must be *definitely assigned* when created because `struct` types directly store their data. The `default` value of a struct *definitely assigned* all fields to 0. All fields must be definitely assigned when a constructor is invoked. You initialize fields using the following mechanisms: - You can add *field initializers* to any field or auto implemented property. - You can initialize any fields, or auto properties, in the body of the constructor. -Beginning with C# 11, if you don't initialize all fields in a struct, the compiler adds code to the constructor that initializes those fields to the default value. The compiler performs its usual definite assignment analysis. Any fields that are accessed before being assigned, or not definitely assigned when the constructor finishes executing are assigned their default values before the constructor body executes. If `this` is accessed before all fields are assigned, the struct is initialized to the default value before the constructor body executes. +Beginning with C# 11, if you don't initialize all fields in a struct, the compiler adds code to the constructor that initializes those fields to the default value. A struct assigned to its `default` value is initialized to the 0-bit pattern. A string initialized with `new` is initialized to the 0-bit pattern, followed by executing any field initializers and a constructor. :::code language="csharp" source="snippets/shared/StructType.cs" id="FieldInitializer"::: Every `struct` has a `public` parameterless constructor. If you write a parameterless constructor, it must be public. If a struct declares any field initializers, it must explicitly declare a constructor. That constructor need not be parameterless. If a struct declares a field initializer but no constructors, the compiler reports an error. Any explicitly declared constructor (with parameters, or parameterless) executes all field initializers for that struct. All fields without a field initializer or an assignment in a constructor are set to the [default value](default-values.md). For more information, see the [Parameterless struct constructors](~/_csharplang/proposals/csharp-10.0/parameterless-struct-constructors.md) feature proposal note. -Beginning with C# 12, `struct` types can define a [primary constructor](../../programming-guide/classes-and-structs/instance-constructors.md#primary-constructors) as part of its declaration. Primary constructors provides a concise syntax for constructor parameters that can be used throughout the `struct` body, in any member declaration for that struct. +Beginning with C# 12, `struct` types can define a [primary constructor](../../programming-guide/classes-and-structs/instance-constructors.md#primary-constructors) as part of its declaration. Primary constructors provide a concise syntax for constructor parameters that can be used throughout the `struct` body, in any member declaration for that struct. If all instance fields of a structure type are accessible, you can also instantiate it without the `new` operator. In that case you must initialize all instance fields before the first use of the instance. The following example shows how to do that: :::code language="csharp" source="snippets/shared/StructType.cs" id="SnippetWithoutNew"::: -In the case of the [built-in value types](value-types.md#built-in-value-types), use the corresponding literals to specify a value of the type. +For the [built-in value types](value-types.md#built-in-value-types), use the corresponding literals to specify a value of the type. ## Limitations with the design of a structure type -Structs have most of the capabilities of a [class](../keywords/class.md) type. There are some exceptions, and some exceptions that have been removed in more recent versions: +Structs have most of the capabilities of a [class](../keywords/class.md) type. There are some exceptions: - A structure type can't inherit from other class or structure type and it can't be the base of a class. However, a structure type can implement [interfaces](../keywords/interface.md). - You can't declare a [finalizer](../../programming-guide/classes-and-structs/finalizers.md) within a structure type. -- Prior to C# 11, a constructor of a structure type must initialize all instance fields of the type. +- Before C# 11, a constructor of a structure type must initialize all instance fields of the type. ## Passing structure-type variables by reference When you pass a structure-type variable to a method as an argument or return a structure-type value from a method, the whole instance of a structure type is copied. Pass by value can affect the performance of your code in high-performance scenarios that involve large structure types. You can avoid value copying by passing a structure-type variable by reference. Use the `ref`, `out`, `in`, or `ref readonly` method parameter modifiers to indicate that an argument must be [passed by reference](../keywords/method-parameters.md#reference-parameters). Use [ref returns](../statements/jump-statements.md#the-return-statement) to return a method result by reference. For more information, see [Avoid allocations](../../advanced-topics/performance/index.md). -## struct constraint +## `struct` constraint You also use the `struct` keyword in the [`struct` constraint](../../programming-guide/generics/constraints-on-type-parameters.md) to specify that a type parameter is a non-nullable value type. Both structure and [enumeration](enum.md) types satisfy the `struct` constraint. @@ -174,10 +174,10 @@ For more information about `struct` features, see the following feature proposal - [Readonly structs](~/_csharplang/proposals/csharp-7.2/readonly-ref.md#readonly-structs) - [Readonly instance members](~/_csharplang/proposals/csharp-8.0/readonly-instance-members.md) -- [C# 10 - Parameterless struct constructors](~/_csharplang/proposals/csharp-10.0/parameterless-struct-constructors.md) -- [C# 10 - Allow `with` expression on structs](~/_csharplang/proposals/csharp-10.0/record-structs.md#allow-with-expression-on-structs) -- [C# 10 - Record structs](~/_csharplang/proposals/csharp-10.0/record-structs.md) -- [C# 11 - Auto default structs](~/_csharplang/proposals/csharp-11.0/auto-default-structs.md) +- [Parameterless struct constructors](~/_csharplang/proposals/csharp-10.0/parameterless-struct-constructors.md) +- [Allow `with` expression on structs](~/_csharplang/proposals/csharp-10.0/record-structs.md#allow-with-expression-on-structs) +- [Record structs](~/_csharplang/proposals/csharp-10.0/record-structs.md) +- [Auto default structs](~/_csharplang/proposals/csharp-11.0/auto-default-structs.md) ## See also diff --git a/docs/csharp/language-reference/compiler-messages/constructor-errors.md b/docs/csharp/language-reference/compiler-messages/constructor-errors.md index 8ab017c638f19..e73072decec89 100644 --- a/docs/csharp/language-reference/compiler-messages/constructor-errors.md +++ b/docs/csharp/language-reference/compiler-messages/constructor-errors.md @@ -89,7 +89,7 @@ helpviewer_keywords: - "CS9124" - "CS9136" - "CS9179" -ms.date: 01/11/2024 +ms.date: 11/22/2024 --- # Resolve errors and warnings in constructor declarations @@ -182,7 +182,7 @@ When a constructor is marked `extern`, the compiler can't guarantee the construc - **CS8982**: *A constructor declared in a 'struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor.* - **CS8983**: *A 'struct' with field initializers must include an explicitly declared constructor.* -Recent features in C# remove earlier restrictions to `struct` types. **CS0568** is generated when you declare a parameterless instance constructor in older versions of C#. Once you're using C# 10, you can declare an explicit parameterless instance constructor. That explicit parameterless constructor must be `public`. If your `struct` declares any [field initializers](../../programming-guide/classes-and-structs/fields.md), you must also declare an explicit instance constructor. This constructor can be a parameterless constructor with an empty body. +Recent features in C# remove earlier restrictions to `struct` types. **CS0568** is generated when you declare a parameterless instance constructor in older versions of C#. You can declare an explicit parameterless instance constructor in newer versions of C#. That explicit parameterless constructor must be `public`. If your `struct` declares any [field initializers](../../programming-guide/classes-and-structs/fields.md), you must also declare an explicit instance constructor. This constructor can be a parameterless constructor with an empty body. When a `struct` type declares a primary constructor, including `record struct` types, all other instance constructors except a parameterless constructor must call the primary constructor or another explicitly declared constructor using `this()`. diff --git a/docs/csharp/language-reference/compiler-messages/parameter-argument-mismatch.md b/docs/csharp/language-reference/compiler-messages/parameter-argument-mismatch.md index dff8a9973aec7..5c53c0c591b3e 100644 --- a/docs/csharp/language-reference/compiler-messages/parameter-argument-mismatch.md +++ b/docs/csharp/language-reference/compiler-messages/parameter-argument-mismatch.md @@ -171,7 +171,7 @@ The compiler issues the following errors when you specified an [interpolated str - **CS8950**: *Parameter is an argument to the interpolated string handler conversion on parameter, but the corresponding argument is specified after the interpolated string expression.* - **CS8951**: *Parameter is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter.* -An interpolated string handler is a pattern-based construct. It's important to get the pattern correct. Consult the [feature spec](~/_csharplang/proposals/csharp-10.0/improved-interpolated-strings.md#the-handler-pattern), or follow the tutorial on [building an interpolated string handler](../../whats-new/tutorials/interpolated-string-handler.md). +An interpolated string handler is a pattern-based construct. It's important to get the pattern correct. Consult the [feature spec](~/_csharplang/proposals/csharp-10.0/improved-interpolated-strings.md#the-handler-pattern), or follow the tutorial on [building an interpolated string handler](../../advanced-topics/performance/interpolated-string-handler.md). ## Caller debugging information diff --git a/docs/csharp/language-reference/configure-language-version.md b/docs/csharp/language-reference/configure-language-version.md index 6724333aee5c9..5c61af79d3717 100644 --- a/docs/csharp/language-reference/configure-language-version.md +++ b/docs/csharp/language-reference/configure-language-version.md @@ -58,6 +58,10 @@ Builds in all subdirectories of the directory containing that file now use the p ## C# language version reference +> [!IMPORTANT] +> +> Using a C# language version newer than the version associated with your target TFM is unsupported. + The following table shows all current C# language versions. Older compilers might not understand every value. If you install the latest .NET SDK, you have access to everything listed. [!INCLUDE [langversion-table](includes/langversion-table.md)] diff --git a/docs/csharp/language-reference/index.yml b/docs/csharp/language-reference/index.yml index 71ea416d4ce7c..d226dc39f1292 100644 --- a/docs/csharp/language-reference/index.yml +++ b/docs/csharp/language-reference/index.yml @@ -37,8 +37,6 @@ landingContent: url: ../whats-new/csharp-12.md - text: "What's new in C# 11" url: ../whats-new/csharp-11.md - - text: "What's new in C# 10" - url: ../whats-new/csharp-10.md - linkListType: reference links: - text: Breaking changes in the C# compiler diff --git a/docs/csharp/language-reference/keywords/const.md b/docs/csharp/language-reference/keywords/const.md index ad71a1adde657..abce400e9d4f4 100644 --- a/docs/csharp/language-reference/keywords/const.md +++ b/docs/csharp/language-reference/keywords/const.md @@ -1,17 +1,16 @@ --- -description: "const keyword - C# Reference" -title: "const keyword" -ms.date: 06/20/2021 +description: "Declare compile time constants with the `const` keyword" +title: "The const keyword" +ms.date: 11/22/2024 f1_keywords: - "const_CSharpKeyword" - "const" helpviewer_keywords: - "const keyword [C#]" -ms.assetid: 79eb447c-117b-4418-933f-97c50aa472db --- -# const (C# Reference) +# The const keyword -You use the `const` keyword to declare a constant field or a local constant. Constant fields and locals aren't variables and may not be modified. Constants can be numbers, Boolean values, strings, or a null reference. Don’t create a constant to represent information that you expect to change at any time. For example, don’t use a constant field to store the price of a service, a product version number, or the brand name of a company. These values can change over time, and because compilers propagate constants, other code compiled with your libraries will have to be recompiled to see the changes. See also the [readonly](readonly.md) keyword. For example: +You use the `const` keyword to declare a constant field or a local constant. Constant fields and locals aren't variables and can't be modified. Constants can be numbers, Boolean values, strings, or a null reference. Don’t create a constant to represent information that you expect to change at any time. For example, don’t use a constant field to store the price of a service, a product version number, or the brand name of a company. These values can change over time, and because compilers propagate constants, other code compiled with your libraries will have to be recompiled to see the changes. See also the [readonly](readonly.md) keyword. For example: ```csharp const int X = 0; @@ -19,7 +18,7 @@ public const double GravitationalConstant = 6.673e-11; private const string ProductName = "Visual C#"; ``` -Beginning with C# 10, [interpolated strings](../tokens/interpolated.md) may be constants, if all expressions used are also constant strings. This feature can improve the code that builds constant strings: +[Interpolated strings](../tokens/interpolated.md) can be constants, if all expressions used are also constant strings. This feature can improve the code that builds constant strings: ```csharp const string Language = "C#"; @@ -39,7 +38,7 @@ The constant declaration can declare multiple constants, such as: public const double X = 1.0, Y = 2.0, Z = 3.0; ``` -The `static` modifier is not allowed in a constant declaration. +The `static` modifier isn't allowed in a constant declaration. A constant can participate in a constant expression, as follows: diff --git a/docs/csharp/language-reference/keywords/namespace.md b/docs/csharp/language-reference/keywords/namespace.md index 04216236c887e..fdea80074230f 100644 --- a/docs/csharp/language-reference/keywords/namespace.md +++ b/docs/csharp/language-reference/keywords/namespace.md @@ -1,31 +1,30 @@ --- -description: "namespace keyword - C# Reference" -title: "namespace keyword" -ms.date: 08/19/2021 +description: "Organize related types and functionality using the namespace keyword - C# Reference" +title: "The namespace keyword" +ms.date: 11/22/2024 f1_keywords: - "namespace_CSharpKeyword" - "namespace" helpviewer_keywords: - "namespace keyword [C#]" - "scope [C#]" -ms.assetid: 0a788423-9110-42e0-97d9-bda41ca4870f --- -# namespace +# The `namespace` keyword The `namespace` keyword is used to declare a scope that contains a set of related objects. You can use a namespace to organize code elements and to create globally unique types. :::code language="csharp" source="snippets/csrefKeywordsNamespace.cs" id="Snippet1"::: -*File scoped namespace declarations* enable you to declare that all types in a file are in a single namespace. File scoped namespace declarations are available with C# 10. The following example is similar to the previous example, but uses a file scoped namespace declaration: +*File scoped namespace declarations* enable you to declare that all types in a file are in a single namespace. The following example is similar to the previous example, but uses a file scoped namespace declaration: :::code language="csharp" source="snippets/filescopednamespace.cs" ::: ## Using Statements in File Scoped Namespaces -When using *file-scoped namespaces*, the placement of `using` statements affects their scope within the file. File-scoped namespaces lower to the equivalent traditional namespace declaration that ends with a closing bracket at the end of the file. This behavior determines where `using` directives are applied as follows: +When you use *file-scoped namespaces*, the placement of `using` statements affects their scope within the file. File-scoped namespaces lower to the equivalent traditional namespace declaration that ends with a closing bracket at the end of the file. This behavior determines where `using` directives are applied as follows: -- If the `using` statements are placed before the file-scoped namespace declaration, they are treated as being outside of the namespace and are interpreted as fully-qualified namespaces. -- If the `using` statements are placed after the file-scoped namespace declaration, they are scoped within the namespace itself. +- If the `using` statements are placed before the file-scoped namespace declaration, they're treated as being outside of the namespace and are interpreted as fully qualified namespaces. +- If the `using` statements are placed after the file-scoped namespace declaration, they're scoped within the namespace itself. For example: @@ -44,9 +43,9 @@ public class SampleClass } ``` -In the above example, `System` is globally accessible, while `System.Text` applies only within `SampleNamespace`. +In the preceding example, `System` is globally accessible, while `System.Text` applies only within `SampleNamespace`. -The preceding example doesn't include a nested namespace. File scoped namespaces can't include additional namespace declarations. You cannot declare a nested namespace or a second file-scoped namespace: +The preceding example doesn't include a nested namespace. File scoped namespaces can't include more namespace declarations. You can't declare a nested namespace or a second file-scoped namespace: ```csharp namespace SampleNamespace; diff --git a/docs/csharp/language-reference/keywords/using-directive.md b/docs/csharp/language-reference/keywords/using-directive.md index 4ec50fd446fd3..1f61b22073cc1 100644 --- a/docs/csharp/language-reference/keywords/using-directive.md +++ b/docs/csharp/language-reference/keywords/using-directive.md @@ -1,7 +1,7 @@ --- description: "The `using` directive imports types from a namespace, or creates an alias for a given type. Using directives enable you to use simple names for types instead of the fully qualified type name." title: "The using directive: Import types from a namespace" -ms.date: 11/14/2024 +ms.date: 11/24/2024 f1_keywords: - "using_CSharpKeyword" helpviewer_keywords: @@ -17,7 +17,7 @@ using System.Text; You can apply two modifiers to a `using` directive: -- The `global` modifier has the same effect as adding the same `using` directive to every source file in your project. This modifier was introduced in C# 10. +- The `global` modifier has the same effect as adding the same `using` directive to every source file in your project. - The `static` modifier imports the `static` members and nested types from a single type rather than importing all the types in a namespace. You can combine both modifiers to import the static members from a type to all source files in your project. @@ -48,7 +48,7 @@ Create a `using` directive to use the types in a namespace without having to spe ## The `global` modifier -Adding the `global` modifier to a `using` directive means that using is applied to all files in the compilation (typically a project). The `global using` directive was added in C# 10. Its syntax is: +Adding the `global` modifier to a `using` directive means that using is applied to all files in the compilation (typically a project): ```csharp global using ; @@ -175,7 +175,7 @@ The `global::` ensures that the namespace lookup for the namespace following the For more information, see [Using directives](~/_csharpstandard/standard/namespaces.md#145-using-directives) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. -For more information on the *global using* modifier, see the [global usings feature specification - C# 10](~/_csharplang/proposals/csharp-10.0/GlobalUsingDirective.md). +For more information on the *global using* modifier, see the [global usings feature specification](~/_csharplang/proposals/csharp-10.0/GlobalUsingDirective.md). ## See also diff --git a/docs/csharp/language-reference/language-versioning.md b/docs/csharp/language-reference/language-versioning.md index 41a3c718fd766..54b9e4832dd47 100644 --- a/docs/csharp/language-reference/language-versioning.md +++ b/docs/csharp/language-reference/language-versioning.md @@ -9,7 +9,7 @@ ms.date: 09/17/2024 The latest C# compiler determines a default language version based on your project's target framework or frameworks. Visual Studio doesn't provide a UI to change the value, but you can change it by editing the *csproj* file. The choice of default ensures that you use the latest language version compatible with your target framework. You benefit from access to the latest language features compatible with your project's target. This default choice also ensures you don't use a language that requires types or runtime behavior not available in your target framework. Choosing a language version newer than the default can cause hard to diagnose compile-time and runtime errors. -[C# 13](../whats-new/csharp-13.md) is supported only on .NET 9 and newer versions. [C# 12](../whats-new/csharp-12.md) is supported only on .NET 8 and newer versions. [C# 11](../whats-new/csharp-11.md) is supported only on .NET 7 and newer versions. +[C# 13](../whats-new/csharp-13.md) is supported only on .NET 9 and newer versions. [C# 12](../whats-new/csharp-12.md) is supported only on .NET 8 and newer versions. [C# 11](../whats-new/csharp-11.md) is supported only on .NET 7 and newer versions. Using a C# language version newer than the version associated with your target TFM is unsupported. Check the [Visual Studio platform compatibility](/visualstudio/releases/2022/compatibility#-visual-studio-2022-support-for-net-development) page for details on which .NET versions are supported by versions of Visual Studio. Check the [Mono page for C#](https://www.mono-project.com/docs/about-mono/languages/csharp/) for Mono compatibility with C# versions. diff --git a/docs/csharp/language-reference/operators/addition-operator.md b/docs/csharp/language-reference/operators/addition-operator.md index 578bd1daa1f7c..6581909a498c1 100644 --- a/docs/csharp/language-reference/operators/addition-operator.md +++ b/docs/csharp/language-reference/operators/addition-operator.md @@ -1,7 +1,7 @@ --- title: "Addition operators - + and +=" description: "The C# addition operators (`+`, and `+=`) work with operands of numeric, string, or delegate types." -ms.date: 11/29/2022 +ms.date: 11/22/2024 f1_keywords: - "+_CSharpKeyword" - "+=_CSharpKeyword" @@ -30,7 +30,7 @@ When one or both operands are of type [string](../builtin-types/reference-types. [!code-csharp-interactive[string interpolation](snippets/shared/AdditionOperator.cs#UseStringInterpolation)] -Beginning with C# 10, you can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. +You can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. Beginning with C# 11, the `+` operator performs string concatenation for UTF-8 literal strings. This operator concatenates two `ReadOnlySpan` objects. @@ -52,13 +52,13 @@ An expression using the `+=` operator, such as x += y ``` -is equivalent to +Is equivalent to: ```csharp x = x + y ``` -except that `x` is only evaluated once. +Except that `x` is only evaluated once. The following example demonstrates the usage of the `+=` operator: diff --git a/docs/csharp/language-reference/operators/lambda-expressions.md b/docs/csharp/language-reference/operators/lambda-expressions.md index 4c71ef29f4d4c..7f334b8dd5bc0 100644 --- a/docs/csharp/language-reference/operators/lambda-expressions.md +++ b/docs/csharp/language-reference/operators/lambda-expressions.md @@ -1,7 +1,7 @@ --- title: "Lambda expressions - Lambda expressions and anonymous functions" description: C# lambda expressions that are used to create anonymous functions and expression bodied members. -ms.date: 05/17/2024 +ms.date: 11/22/2024 helpviewer_keywords: - "lambda expressions [C#]" - "outer variables [C#]" @@ -228,7 +228,7 @@ The general rules for type inference for lambdas are as follows: A lambda expression in itself doesn't have a type because the common type system has no intrinsic concept of "lambda expression." However, it's sometimes convenient to speak informally of the "type" of a lambda expression. That informal "type" refers to the delegate type or type to which the lambda expression is converted. -Beginning with C# 10, a lambda expression can have a *natural type*. Instead of forcing you to declare a delegate type, such as `Func<...>` or `Action<...>` for a lambda expression, the compiler can infer the delegate type from the lambda expression. For example, consider the following declaration: +A lambda expression can have a *natural type*. Instead of forcing you to declare a delegate type, such as `Func<...>` or `Action<...>` for a lambda expression, the compiler can infer the delegate type from the lambda expression. For example, consider the following declaration: ```csharp var parse = (string s) => int.Parse(s); @@ -275,7 +275,7 @@ Typically, the return type of a lambda expression is obvious and inferred. For s var choose = (bool b) => b ? 1 : "two"; // ERROR: Can't infer return type ``` -Beginning with C# 10, you can specify the return type of a lambda expression before the input parameters. When you specify an explicit return type, you must parenthesize the input parameters: +You can specify the return type of a lambda expression before the input parameters. When you specify an explicit return type, you must parenthesize the input parameters: ```csharp var choose = object (bool b) => b ? 1 : "two"; // Func @@ -283,7 +283,7 @@ var choose = object (bool b) => b ? 1 : "two"; // Func ## Attributes -Beginning with C# 10, you can add attributes to a lambda expression and its parameters. The following example shows how to add attributes to a lambda expression: +You can add attributes to a lambda expression and its parameters. The following example shows how to add attributes to a lambda expression: ```csharp Func parse = [ProvidesNullCheck] (s) => (s is not null) ? int.Parse(s) : null; @@ -329,7 +329,7 @@ For more information about these features, see the following feature proposal no - [Lambda discard parameters](~/_csharplang/proposals/csharp-9.0/lambda-discard-parameters.md) - [Static anonymous functions](~/_csharplang/proposals/csharp-9.0/static-anonymous-functions.md) -- [Lambda improvements (C# 10)](~/_csharplang/proposals/csharp-10.0/lambda-improvements.md) +- [Lambda improvements](~/_csharplang/proposals/csharp-10.0/lambda-improvements.md) ## See also diff --git a/docs/csharp/language-reference/operators/patterns.md b/docs/csharp/language-reference/operators/patterns.md index e086414bf4b95..a385c5d686fe4 100644 --- a/docs/csharp/language-reference/operators/patterns.md +++ b/docs/csharp/language-reference/operators/patterns.md @@ -1,7 +1,7 @@ --- title: "Patterns - Pattern matching using the is and switch expressions." description: "Learn about the patterns supported by the `is` and `switch` expressions. Combine multiple patterns using the `and`, `or`, and `not` operators." -ms.date: 11/14/2024 +ms.date: 11/22/2024 f1_keywords: - "and_CSharpKeyword" - "or_CSharpKeyword" @@ -190,7 +190,7 @@ A property pattern is a recursive pattern. That is, you can use any pattern as a The preceding example uses the `or` [pattern combinator](#logical-patterns) and [record types](../builtin-types/record.md). -Beginning with C# 10, you can reference nested properties or fields within a property pattern. This capability is known as an *extended property pattern*. For example, you can refactor the method from the preceding example into the following equivalent code: +You can reference nested properties or fields within a property pattern. This capability is known as an *extended property pattern*. For example, you can refactor the method from the preceding example into the following equivalent code: :::code language="csharp" source="snippets/patterns/PropertyPattern.cs" id="ExtendedPropertyPattern"::: @@ -304,7 +304,7 @@ For information about features added in C# 8 and later, see the following featur - [Recursive pattern matching](~/_csharplang/proposals/csharp-8.0/patterns.md) - [Pattern-matching updates](~/_csharplang/proposals/csharp-9.0/patterns3.md) -- [C# 10 - Extended property patterns](~/_csharplang/proposals/csharp-10.0/extended-property-patterns.md) +- [Extended property patterns](~/_csharplang/proposals/csharp-10.0/extended-property-patterns.md) - [C# 11 - List patterns](~/_csharplang/proposals/csharp-11.0/list-patterns.md) - [C# 11 - Pattern match `Span` on string literal](~/_csharplang/proposals/csharp-11.0/pattern-match-span-of-char-on-string.md) diff --git a/docs/csharp/language-reference/operators/with-expression.md b/docs/csharp/language-reference/operators/with-expression.md index a1c05b6ccbb11..55ecef93d9ec0 100644 --- a/docs/csharp/language-reference/operators/with-expression.md +++ b/docs/csharp/language-reference/operators/with-expression.md @@ -1,7 +1,7 @@ --- title: "with expression - create new objects that are modified copies of existing objects" description: "Learn about a with expression that performs nondestructive mutation of C# records and structures. The `with` keyword provides the means to modify one or more properties in the new object." -ms.date: 11/28/2022 +ms.date: 11/22/2024 f1_keywords: - "with_CSharpKeyword" helpviewer_keywords: @@ -14,7 +14,7 @@ A `with` expression produces a copy of its operand with the specified properties :::code language="csharp" source="snippets/with-expression/BasicExample.cs" ::: -The left-hand operand of a `with` expression can be of a [record type](../builtin-types/record.md). Beginning with C# 10, a left-hand operand of a `with` expression can also be of a [structure type](../builtin-types/struct.md) or an [anonymous type](../../fundamentals/types/anonymous-types.md). +The left-hand operand of a `with` expression can be of a [record type](../builtin-types/record.md). A left-hand operand of a `with` expression can also be of a [structure type](../builtin-types/struct.md) or an [anonymous type](../../fundamentals/types/anonymous-types.md). The result of a `with` expression has the same run-time type as the expression's operand, as the following example shows: diff --git a/docs/csharp/language-reference/preprocessor-directives.md b/docs/csharp/language-reference/preprocessor-directives.md index 33fc760c023b4..3a79013cec760 100644 --- a/docs/csharp/language-reference/preprocessor-directives.md +++ b/docs/csharp/language-reference/preprocessor-directives.md @@ -1,7 +1,7 @@ --- description: "Learn the different C# preprocessor directives that control conditional compilation, warnings, nullable analysis, and more" title: "Preprocessor directives" -ms.date: 01/30/2023 +ms.date: 11/22/2024 f1_keywords: - "cs.preprocessor" - "#nullable" @@ -274,7 +274,7 @@ A `#line hidden` directive doesn't affect file names or line numbers in error re The `#line filename` directive specifies the file name you want to appear in the compiler output. By default, the actual name of the source code file is used. The file name must be in double quotation marks ("") and must be preceded by a line number. -Beginning with C# 10, you can use a new form of the `#line` directive: +You can use a new form of the `#line` directive: ```csharp #line (1, 1) - (5, 60) 10 "partial-class.cs" diff --git a/docs/csharp/language-reference/tokens/interpolated.md b/docs/csharp/language-reference/tokens/interpolated.md index 2c90e78b89831..e89d08f401e94 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: 07/31/2024 +ms.date: 11/22/2024 f1_keywords: - "$_CSharpKeyword" - "$" @@ -20,7 +20,7 @@ String interpolation provides a more readable, convenient syntax to format strin :::code language="csharp" interactive="try-dotnet-method" source="./snippets/string-interpolation.cs" id="CompareWithCompositeFormatting"::: -Beginning with C# 10, you can use an interpolated string to initialize a [constant](../keywords/const.md) string. You can do that only if all interpolation expressions within the interpolated string are constant strings as well. +You can use an interpolated string to initialize a [constant](../keywords/const.md) string. You can do that only if all interpolation expressions within the interpolated string are constant strings as well. ## Structure of an interpolated string @@ -92,12 +92,12 @@ If you're new to string interpolation, see the [String interpolation in C#](../. ## Compilation of interpolated strings -Beginning with C# 10 and .NET 6, the compiler checks if an interpolated string is assigned to a type that satisfies the [_interpolated string handler pattern_](~/_csharplang/proposals/csharp-10.0/improved-interpolated-strings.md#the-handler-pattern). An _interpolated string handler_ is a type that converts the interpolated string into a result string. When an interpolated string has the type `string`, it's processed by the . For the example of a custom interpolated string handler, see the [Write a custom string interpolation handler](../../whats-new/tutorials/interpolated-string-handler.md) tutorial. Use of an interpolated string handler is an advanced scenario, typically required for performance reasons. +The compiler checks if an interpolated string is assigned to a type that satisfies the [_interpolated string handler pattern_](~/_csharplang/proposals/csharp-10.0/improved-interpolated-strings.md#the-handler-pattern). An _interpolated string handler_ is a type that converts the interpolated string into a result string. When an interpolated string has the type `string`, it's processed by the . For the example of a custom interpolated string handler, see the [Write a custom string interpolation handler](../../advanced-topics/performance/interpolated-string-handler.md) tutorial. Use of an interpolated string handler is an advanced scenario, typically required for performance reasons. > [!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. -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 `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. @@ -105,7 +105,7 @@ If an interpolated string has the type or [!NOTE] - > In C# 10 and later, when an expression of the form `new ValType()` invokes the explicitly defined parameterless constructor of a value type, the compiler generates an error as the default parameter value must be a compile-time constant. Use the `default(ValType)` expression or the `default` literal to provide the default parameter value. For more information about parameterless constructors, see the [Struct initialization and default values](language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](language-reference/builtin-types/struct.md) article. + > When an expression of the form `new ValType()` invokes the explicitly defined parameterless constructor of a value type, the compiler generates an error as the default parameter value must be a compile-time constant. Use the `default(ValType)` expression or the `default` literal to provide the default parameter value. For more information about parameterless constructors, see the [Struct initialization and default values](language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](language-reference/builtin-types/struct.md) article. If a method includes both required and optional parameters, optional parameters are defined at the end of the parameter list, after all required parameters. diff --git a/docs/csharp/misc/cs0573.md b/docs/csharp/misc/cs0573.md index 8051bf8e6fa0d..6d1198ee61f1d 100644 --- a/docs/csharp/misc/cs0573.md +++ b/docs/csharp/misc/cs0573.md @@ -1,7 +1,7 @@ --- description: "Compiler Error CS0573" title: "Compiler Error CS0573" -ms.date: 07/20/2015 +ms.date: 11/22/2024 f1_keywords: - "CS0573" helpviewer_keywords: @@ -15,7 +15,7 @@ ms.assetid: 10ef9625-44f1-4936-ada3-56938357aa01 You can't initialize an instance field of a [struct](../language-reference/builtin-types/struct.md). Fields of value types will be initialized to their default values, and reference-type fields will be initialized to `null`. > [!NOTE] -> Beginning with C# 10, you can initialize a struct's instance field or property at its declaration. For more information, see the [Struct initialization and default values](../language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](../language-reference/builtin-types/struct.md) article. +> You can initialize a struct's instance field or property at its declaration. For more information, see the [Struct initialization and default values](../language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](../language-reference/builtin-types/struct.md) article. ## Example diff --git a/docs/csharp/nullable-references.md b/docs/csharp/nullable-references.md index c1fe093472d0b..363263380aa99 100644 --- a/docs/csharp/nullable-references.md +++ b/docs/csharp/nullable-references.md @@ -2,7 +2,7 @@ title: Nullable reference types description: This article provides an overview of nullable reference types. Learn how the feature provides safety against null reference exceptions, for new and existing projects. ms.subservice: null-safety -ms.date: 01/16/2024 +ms.date: 11/22/2024 --- # Nullable reference types @@ -125,9 +125,6 @@ You can address these warnings in one of two ways: *Constructor chaining*, or *n :::code language="csharp" source="./language-reference/compiler-messages/snippets/null-warnings/PersonExamples.cs" id="ConstructorChainingAndMemberNotNull"::: -> [!NOTE] -> A number of improvements to definite assignment and null-state analysis were added in C# 10. When you upgrade to C# 10, you may find fewer nullable warnings that are false positives. You can learn more about the improvements in the [features specification for definite assignment improvements](~/_csharplang/proposals/csharp-10.0/improved-definite-assignment.md). - Nullable state analysis and the warnings the compiler generates help you avoid program errors by dereferencing `null`. The article on [resolving nullable warnings](language-reference/compiler-messages/nullable-warnings.md) provides techniques for correcting the warnings most likely seen in your code. ## Attributes on API signatures diff --git a/docs/csharp/programming-guide/classes-and-structs/local-functions.md b/docs/csharp/programming-guide/classes-and-structs/local-functions.md index 37f1f33d848a0..d8571d6778b11 100644 --- a/docs/csharp/programming-guide/classes-and-structs/local-functions.md +++ b/docs/csharp/programming-guide/classes-and-structs/local-functions.md @@ -1,7 +1,7 @@ --- title: "Local functions" description: Local functions in C# are private methods that are nested in another member and can be called from their containing member. -ms.date: 10/16/2020 +ms.date: 11/22/2024 helpviewer_keywords: - "local functions [C#]" --- @@ -23,7 +23,7 @@ However, local functions can't be declared inside an expression-bodied member. > [!NOTE] > In some cases, you can use a lambda expression to implement functionality also supported by a local function. For a comparison, see [Local functions vs. lambda expressions](#local-functions-vs-lambda-expressions). -Local functions make the intent of your code clear. Anyone reading your code can see that the method is not callable except by the containing method. For team projects, they also make it impossible for another developer to mistakenly call the method directly from elsewhere in the class or struct. +Local functions make the intent of your code clear. Anyone reading your code can see that the method isn't callable except by the containing method. For team projects, they also make it impossible for another developer to mistakenly call the method directly from elsewhere in the class or struct. ## Local function syntax @@ -46,13 +46,13 @@ You can use the following modifiers with a local function: All local variables that are defined in the containing member, including its method parameters, are accessible in a non-static local function. -Unlike a method definition, a local function definition cannot include the member access modifier. Because all local functions are private, including an access modifier, such as the `private` keyword, generates compiler error CS0106, "The modifier 'private' is not valid for this item." +Unlike a method definition, a local function definition can't include the member access modifier. Because all local functions are private, including an access modifier, such as the `private` keyword, generates compiler error CS0106, "The modifier 'private' isn't valid for this item." The following example defines a local function named `AppendPathSeparator` that is private to a method named `GetText`: :::code language="csharp" source="snippets/local-functions/Program.cs" id="Basic" ::: -You can apply attributes to a local function, its parameters and type parameters, as the following example shows: +You can apply attributes to a local function, its parameters, and type parameters, as the following example shows: :::code language="csharp" source="snippets/local-functions/Program.cs" id="WithAttributes" ::: @@ -72,7 +72,7 @@ If you put iterator logic into a local function, argument validation exceptions ## Local functions vs. lambda expressions -At first glance, local functions and [lambda expressions](../../language-reference/operators/lambda-expressions.md) are very similar. In many cases, the choice between using lambda expressions and local functions is a [matter of style and personal preference](../../../fundamentals/code-analysis/style-rules/ide0039.md). However, there are real differences in where you can use one or the other that you should be aware of. +At first glance, local functions and [lambda expressions](../../language-reference/operators/lambda-expressions.md) are similar. In many cases, the choice between using lambda expressions and local functions is a [matter of style and personal preference](../../../fundamentals/code-analysis/style-rules/ide0039.md). However, there are real differences in where you can use one or the other that you should be aware of. Let's examine the differences between the local function and lambda expression implementations of the factorial algorithm. Here's the version using a local function: @@ -90,25 +90,25 @@ Local functions are explicitly named like methods. Lambda expressions are anonym Lambda expressions rely on the type of the `Action`/`Func` variable that they're assigned to determine the argument and return types. In local functions, since the syntax is much like writing a normal method, argument types and return type are already part of the function declaration. -Beginning with C# 10, some lambda expressions have a *natural type*, which enables the compiler to infer the return type and parameter types of the lambda expression. +Some lambda expressions have a *natural type*, which enables the compiler to infer the return type and parameter types of the lambda expression. ### Definite assignment -Lambda expressions are objects that are declared and assigned at run time. In order for a lambda expression to be used, it needs to be definitely assigned: the `Action`/`Func` variable that it will be assigned to must be declared and the lambda expression assigned to it. Notice that `LambdaFactorial` must declare and initialize the lambda expression `nthFactorial` before defining it. Not doing so results in a compile time error for referencing `nthFactorial` before assigning it. +Lambda expressions are objects that are declared and assigned at run time. In order for a lambda expression to be used, it needs to be definitely assigned: the `Action`/`Func` variable that it's assigned to must be declared and the lambda expression assigned to it. Notice that `LambdaFactorial` must declare and initialize the lambda expression `nthFactorial` before defining it. Not doing so results in a compile time error for referencing `nthFactorial` before assigning it. -Local functions are defined at compile time. As they're not assigned to variables, they can be referenced from any code location **where it is in scope**; in our first example `LocalFunctionFactorial`, we could declare our local function either above or below the `return` statement and not trigger any compiler errors. +Local functions are defined at compile time. As they're not assigned to variables, they can be referenced from any code location **where it is in scope**; in the first example `LocalFunctionFactorial`, you could declare the local function either before or after the `return` statement and not trigger any compiler errors. -These differences mean that recursive algorithms are easier to create using local functions. You can declare and define a local function that calls itself. Lambda expressions must be declared, and assigned a default value before they can be re-assigned to a body that references the same lambda expression. +These differences mean that recursive algorithms are easier to create using local functions. You can declare and define a local function that calls itself. Lambda expressions must be declared and assigned a default value before they can be reassigned to a body that references the same lambda expression. ### Implementation as a delegate Lambda expressions are converted to delegates when they're declared. Local functions are more flexible in that they can be written like a traditional method *or* as a delegate. Local functions are only converted to delegates when ***used*** as a delegate. -If you declare a local function and only reference it by calling it like a method, it will not be converted to a delegate. +If you declare a local function and only reference it by calling it like a method, it won't be converted to a delegate. ### Variable capture -The rules of [definite assignment](~/_csharpstandard/standard/variables.md#94-definite-assignment) also affect any variables that are captured by the local function or lambda expression. The compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Consider this example: +The rules of [definite assignment](~/_csharpstandard/standard/variables.md#94-definite-assignment) also affect any variables captured by the local function or lambda expression. The compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Consider this example: ```csharp int M() @@ -123,7 +123,7 @@ int M() The compiler can determine that `LocalFunction` definitely assigns `y` when called. Because `LocalFunction` is called before the `return` statement, `y` is definitely assigned at the `return` statement. -Note that when a local function captures variables in the enclosing scope, the local function is implemented using a closure, like delegate types are. +When a local function captures variables in the enclosing scope, the local function is implemented using a closure, like delegate types are. ### Heap allocations @@ -133,9 +133,9 @@ Consider this async example: :::code language="csharp" source="snippets/local-functions/Program.cs" id="AsyncWithLambda" ::: -The closure for this lambda expression contains the `address`, `index` and `name` variables. In the case of local functions, the object that implements the closure may be a `struct` type. That struct type would be passed by reference to the local function. This difference in implementation would save on an allocation. +The closure for this lambda expression contains the `address`, `index`, and `name` variables. For local functions, the object that implements the closure can be a `struct` type. That struct type would be passed by reference to the local function. This difference in implementation would save on an allocation. -The instantiation necessary for lambda expressions means extra memory allocations, which may be a performance factor in time-critical code paths. Local functions do not incur this overhead. In the example above, the local functions version has two fewer allocations than the lambda expression version. +The instantiation necessary for lambda expressions means extra memory allocations, which might be a performance factor in time-critical code paths. Local functions don't incur this overhead. If you know that your local function won't be converted to a delegate and none of the variables captured by it are captured by other lambdas or local functions that are converted to delegates, you can guarantee that your local function avoids being allocated on the heap by declaring it as a `static` local function. @@ -153,9 +153,9 @@ One final advantage not demonstrated in this sample is that local functions can :::code language="csharp" source="snippets/local-functions/Program.cs" id="YieldReturn" ::: -The `yield return` statement is not allowed in lambda expressions. For more information, see [compiler error CS1621](../../language-reference/compiler-messages/lambda-expression-errors.md#syntax-limitations-in-lambda-expressions). +The `yield return` statement isn't allowed in lambda expressions. For more information, see [compiler error CS1621](../../language-reference/compiler-messages/lambda-expression-errors.md#syntax-limitations-in-lambda-expressions). -While local functions may seem redundant to lambda expressions, they actually serve different purposes and have different uses. Local functions are more efficient for the case when you want to write a function that is called only from the context of another method. +While local functions can seem redundant to lambda expressions, they actually serve different purposes and have different uses. Local functions are more efficient for the case when you want to write a function that is called only from the context of another method. ## C# language specification diff --git a/docs/csharp/programming-guide/classes-and-structs/using-constructors.md b/docs/csharp/programming-guide/classes-and-structs/using-constructors.md index 61ba03ee5a616..e28c88aae29e5 100644 --- a/docs/csharp/programming-guide/classes-and-structs/using-constructors.md +++ b/docs/csharp/programming-guide/classes-and-structs/using-constructors.md @@ -1,7 +1,7 @@ --- title: "Using Constructors" description: This example shows how a class is instantiated by using the new operator in C#. The simple constructor is invoked after memory is allocated for the new object. -ms.date: 05/25/2023 +ms.date: 11/22/2024 helpviewer_keywords: - "constructors [C#], about constructors" --- @@ -23,7 +23,7 @@ You can prevent a class from being instantiated by making the constructor privat For more information, see [Private Constructors](./private-constructors.md). -Constructors for [struct](../../language-reference/builtin-types/struct.md) types resemble class constructors. When a struct type is instantiated with `new`, a constructor is invoked. When a `struct` is set to its `default` value, the runtime initializes all memory in the struct to 0. Prior to C# 10, `structs` can't contain an explicit parameterless constructor because one is provided automatically by the compiler. For more information, see the [Struct initialization and default values](../../language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](../../language-reference/builtin-types/struct.md) article. +Constructors for [struct](../../language-reference/builtin-types/struct.md) types resemble class constructors. When a struct type is instantiated with `new`, a constructor is invoked. When a `struct` is set to its `default` value, the runtime initializes all memory in the struct to 0. If you declare any field initializers in a `struct` type, you must supply a parameterless constructor. For more information, see the [Struct initialization and default values](../../language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](../../language-reference/builtin-types/struct.md) article. The following code uses the parameterless constructor for , so that you're assured that the integer is initialized: diff --git a/docs/csharp/programming-guide/delegates/delegates-with-named-vs-anonymous-methods.md b/docs/csharp/programming-guide/delegates/delegates-with-named-vs-anonymous-methods.md index 27f8c3ecbe746..2b226f93de07b 100644 --- a/docs/csharp/programming-guide/delegates/delegates-with-named-vs-anonymous-methods.md +++ b/docs/csharp/programming-guide/delegates/delegates-with-named-vs-anonymous-methods.md @@ -1,7 +1,7 @@ --- title: "Delegates with Named vs. Anonymous Methods" description: Learn about delegates with named vs. anonymous methods. See code examples and view additional available resources. -ms.date: 11/08/2021 +ms.date: 11/22/2024 helpviewer_keywords: - "delegates [C#], with named vs. anonymous methods" - "methods [C#], in delegates" @@ -20,7 +20,7 @@ The method that you pass as a delegate parameter must have the same signature as > [!NOTE] > Although the delegate can use an [out](../../language-reference/keywords/method-parameters.md#out-parameter-modifier) parameter, we do not recommend its use with multicast event delegates because you cannot know which delegate will be called. -Beginning with C# 10, method groups with a single overload have a *natural type*. This means the compiler can infer the return type and parameter types for the delegate type: +Method groups with a single overload have a *natural type*. This means the compiler can infer the return type and parameter types for the delegate type: ```csharp var read = Console.Read; // Just one overload; Func inferred diff --git a/docs/csharp/programming-guide/strings/index.md b/docs/csharp/programming-guide/strings/index.md index 04825ff9c7833..44145108a3f0c 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: 07/31/2024 +ms.date: 11/22/2024 helpviewer_keywords: - "C# language, strings" - "strings [C#]" @@ -112,7 +112,7 @@ Use string interpolation to improve the readability and maintainability of your :::code language="csharp" source="./snippets/StringInterpolation.cs" id="StringInterpolation"::: -Beginning with C# 10, you can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. +You can use string interpolation to initialize a constant string when all the expressions used for placeholders are also constant strings. Beginning with C# 11, you can combine *raw string literals* with string interpolations. You start and end the format string with three or more successive double quotes. If your output string should contain the `{` or `}` character, you can use extra `$` characters to specify how many `{` and `}` characters start and end an interpolation. Any sequence of fewer `{` or `}` characters is included in the output. The following example shows how you can use that feature to display the distance of a point from the origin, and place the point inside braces: diff --git a/docs/csharp/toc.yml b/docs/csharp/toc.yml index 7d41703aebc6a..3dd86f5570d53 100644 --- a/docs/csharp/toc.yml +++ b/docs/csharp/toc.yml @@ -164,12 +164,8 @@ items: href: whats-new/csharp-11.md - name: Breaking changes since C# 10 href: ../../_roslyn/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20DotNet%207.md - - name: C# 10 - items: - - name: New features in C# 10 - href: whats-new/csharp-10.md - - name: Breaking changes since earlier versions - href: whats-new/breaking-changes.md + - name: Breaking before C# 10 + href: whats-new/breaking-changes.md - name: C# version history href: whats-new/csharp-version-history.md - name: Relationships to .NET library @@ -182,10 +178,6 @@ items: href: whats-new/tutorials/primary-constructors.md - name: Explore static interface members href: whats-new/tutorials/static-virtual-interface-members.md - - name: Explore patterns in objects - href: whats-new/tutorials/patterns-objects.md - - name: Write a custom string interpolation handler - href: whats-new/tutorials/interpolated-string-handler.md - name: Tutorials items: - name: Explore record types @@ -200,6 +192,8 @@ items: href: tutorials/exploration/interpolated-strings.yml - name: Explore string interpolation - in your environment href: tutorials/exploration/interpolated-strings-local.md + - name: Explore patterns in objects + href: tutorials/patterns-objects.md - name: Advanced scenarios for string Interpolation href: tutorials/string-interpolation.md - name: Console Application @@ -407,6 +401,8 @@ items: href: advanced-topics/performance/index.md - name: Tutorial - reduce allocations href: advanced-topics/performance/ref-tutorial.md + - name: Tutorial - Write a custom string interpolation handler + href: advanced-topics/performance/interpolated-string-handler.md - name: The .NET Compiler Platform SDK (Roslyn APIs) items: - name: The .NET Compiler Platform SDK (Roslyn APIs) overview diff --git a/docs/csharp/tour-of-csharp/index.yml b/docs/csharp/tour-of-csharp/index.yml index f91b4f8b0fbff..60a3fadfd5ec0 100644 --- a/docs/csharp/tour-of-csharp/index.yml +++ b/docs/csharp/tour-of-csharp/index.yml @@ -85,18 +85,12 @@ landingContent: url: ../whats-new/csharp-12.md - text: "What's new in C# 11" url: ../whats-new/csharp-11.md - - text: "What's new in C# 10" - url: ../whats-new/csharp-10.md - linkListType: tutorial links: - text: Explore record types url: ../whats-new/tutorials/records.md - text: Explore top level statements url: ../whats-new/tutorials/top-level-statements.md - - text: Explore new patterns - url: ../whats-new/tutorials/patterns-objects.md - - text: Write a custom string interpolation handler - url: ../whats-new/tutorials/interpolated-string-handler.md - linkListType: reference links: - text: Breaking changes in the C# compiler diff --git a/docs/csharp/whats-new/tutorials/patterns-objects.md b/docs/csharp/tutorials/patterns-objects.md similarity index 52% rename from docs/csharp/whats-new/tutorials/patterns-objects.md rename to docs/csharp/tutorials/patterns-objects.md index 9484a71981340..4bee55f93a004 100644 --- a/docs/csharp/whats-new/tutorials/patterns-objects.md +++ b/docs/csharp/tutorials/patterns-objects.md @@ -1,13 +1,13 @@ --- title: Use patterns in objects C# tutorial description: This tutorial teaches you techniques to use pattern matching in class members to create better models for object behavior -ms.date: 11/05/2020 +ms.date: 11/22/2024 --- # Use pattern matching to build your class behavior for better code The pattern matching features in C# provide syntax to express your algorithms. You can use these techniques to implement the behavior in your classes. You can combine object-oriented class design with a data-oriented implementation to provide concise code while modeling real-world objects. -In this tutorial, you'll learn how to: +In this tutorial, you learn how to: > [!div class="checklist"] > @@ -17,39 +17,39 @@ In this tutorial, you'll learn how to: ## Prerequisites -You'll need to set up your machine to run .NET. Download [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) or the [.NET SDK](https://dotnet.microsoft.com/download). +You need to set up your machine to run .NET. Download [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) or the [.NET SDK](https://dotnet.microsoft.com/download). ## Build a simulation of a canal lock -In this tutorial, you'll build a C# class that simulates a [canal lock](https://en.wikipedia.org/wiki/Lock_(water_navigation)). Briefly, a canal lock is a device that raises and lowers boats as they travel between two stretches of water at different levels. A lock has two gates and some mechanism to change the water level. +In this tutorial, you build a C# class that simulates a [canal lock](https://en.wikipedia.org/wiki/Lock_(water_navigation)). Briefly, a canal lock is a device that raises and lowers boats as they travel between two stretches of water at different levels. A lock has two gates and some mechanism to change the water level. -In its normal operation, a boat enters one of the gates while the water level in the lock matches the water level on the side the boat enters. Once in the lock, the water level is changed to match the water level where the boat will leave the lock. Once the water level matches that side, the gate on the exit side opens. Safety measures make sure an operator can't create a dangerous situation in the canal. The water level can be changed only when both gates are closed. At most one gate can be open. To open a gate, the water level in the lock must match the water level outside the gate being opened. +In its normal operation, a boat enters one of the gates while the water level in the lock matches the water level on the side the boat enters. Once in the lock, the water level is changed to match the water level where the boat leaves the lock. Once the water level matches that side, the gate on the exit side opens. Safety measures make sure an operator can't create a dangerous situation in the canal. The water level can be changed only when both gates are closed. At most one gate can be open. To open a gate, the water level in the lock must match the water level outside the gate being opened. You can build a C# class to model this behavior. A `CanalLock` class would support commands to open or close either gate. It would have other commands to raise or lower the water. The class should also support properties to read the current state of both gates and the water level. Your methods implement the safety measures. ## Define a class -You'll build a console application to test your `CanalLock` class. Create a new console project for .NET 5 using either Visual Studio or the .NET CLI. Then, add a new class and name it `CanalLock`. Next, design your public API, but leave the methods not implemented: +You build a console application to test your `CanalLock` class. Create a new console project for .NET 5 using either Visual Studio or the .NET CLI. Then, add a new class and name it `CanalLock`. Next, design your public API, but leave the methods not implemented: -:::code language="csharp" source="snippets/pattern-objects/InterimSteps.cs" ID="APIDesign"::: +:::code language="csharp" source="./snippets/patterns-objects/InterimSteps.cs" ID="APIDesign"::: The preceding code initializes the object so both gates are closed, and the water level is low. Next, write the following test code in your `Main` method to guide you as you create a first implementation of the class: -:::code language="csharp" source="snippets/pattern-objects/Program.cs" ID="HappyTests"::: +:::code language="csharp" source="snippets/patterns-objects/Program.cs" ID="HappyTests"::: -Next, add a first implementation of each method in the `CanalLock` class. The following code implements the methods of the class without concern to the safety rules. You'll add safety tests later: +Next, add a first implementation of each method in the `CanalLock` class. The following code implements the methods of the class without concern to the safety rules. You add safety tests later: -:::code language="csharp" source="snippets/pattern-objects/InterimSteps.cs" ID="FirstImplementation"::: +:::code language="csharp" source="snippets/patterns-objects/InterimSteps.cs" ID="FirstImplementation"::: -The tests you've written so far pass. You've implemented the basics. Now, write a test for the first failure condition. At the end of the previous tests, both gates are closed, and the water level is set to low. Add a test to try opening the upper gate: +The tests you wrote so far pass. You implemented the basics. Now, write a test for the first failure condition. At the end of the previous tests, both gates are closed, and the water level is set to low. Add a test to try opening the upper gate: -:::code language="csharp" source="snippets/pattern-objects/Program.cs" ID="HighGateSafetyTest"::: +:::code language="csharp" source="snippets/patterns-objects/Program.cs" ID="HighGateSafetyTest"::: This test fails because the gate opens. As a first implementation, you could fix it with the following code: -:::code language="csharp" source="snippets/pattern-objects/InterimSteps.cs" ID="SecondImplementation"::: +:::code language="csharp" source="snippets/patterns-objects/InterimSteps.cs" ID="SecondImplementation"::: -Your tests pass. But, as you add more tests, you'll add more and more `if` clauses and test different properties. Soon, these methods will get too complicated as you add more conditionals. +Your tests pass. But, as you add more tests, you add more `if` clauses and test different properties. Soon, these methods get too complicated as you add more conditionals. ## Implement the commands with patterns @@ -66,17 +66,17 @@ A better way is to use *patterns* to determine if the object is in a valid state | Open | Open | High | Open | | ~~Open~~ | ~~Open~~ | ~~Low~~ | ~~Closed (Error)~~ | -The fourth and last rows in the table have strike through text because they're invalid. The code you're adding now should make sure the high water gate is never opened when the water is low. Those states can be coded as a single switch expression (remember that `false` indicates "Closed"): +The fourth and last rows in the table have strike through text because they're invalid. The code you're adding now should make sure the high water gate is never opened when the water is low. Those states can be coded as a single switch expression (remember that `false` indicates "Closed"): -:::code language="csharp" source="snippets/pattern-objects/InterimSteps.cs" ID="ThirdImplementation"::: +:::code language="csharp" source="snippets/patterns-objects/InterimSteps.cs" ID="ThirdImplementation"::: -Try this version. Your tests pass, validating the code. The full table shows the possible combinations of inputs and results. That means you and other developers can quickly look at the table and see that you've covered all the possible inputs. Even easier, the compiler can help as well. After you add the previous code, you can see that the compiler generates a warning: *CS8524* indicates the switch expression doesn't cover all possible inputs. The reason for that warning is that one of the inputs is an `enum` type. The compiler interprets "all possible inputs" as all inputs from the underlying type, typically an `int`. This `switch` expression only checks the values declared in the `enum`. To remove the warning, you can add a catch-all discard pattern for the last arm of the expression. This condition throws an exception, because it indicates invalid input: +Try this version. Your tests pass, validating the code. The full table shows the possible combinations of inputs and results. That means you and other developers can quickly look at the table and see that you covered all the possible inputs. Even easier, the compiler can help as well. After you add the previous code, you can see that the compiler generates a warning: *CS8524* indicates the switch expression doesn't cover all possible inputs. The reason for that warning is that one of the inputs is an `enum` type. The compiler interprets "all possible inputs" as all inputs from the underlying type, typically an `int`. This `switch` expression only checks the values declared in the `enum`. To remove the warning, you can add a catch-all discard pattern for the last arm of the expression. This condition throws an exception, because it indicates invalid input: ```csharp _ => throw new InvalidOperationException("Invalid internal state"), ``` -The preceding switch arm must be last in your `switch` expression because it matches all inputs. Experiment by moving it earlier in the order. That causes a compiler error *CS8510* for unreachable code in a pattern. The natural structure of switch expressions enables the compiler to generate errors and warnings for possible mistakes. The compiler "safety net" makes it easier for you to create correct code in fewer iterations, and the freedom to combine switch arms with wildcards. The compiler will issue errors if your combination results in unreachable arms you didn't expect, and warnings if you remove an arm that's needed. +The preceding switch arm must be last in your `switch` expression because it matches all inputs. Experiment by moving it earlier in the order. That causes a compiler error *CS8510* for unreachable code in a pattern. The natural structure of switch expressions enables the compiler to generate errors and warnings for possible mistakes. The compiler "safety net" makes it easier for you to create correct code in fewer iterations, and the freedom to combine switch arms with wildcards. The compiler issues errors if your combination results in unreachable arms you didn't expect, and warnings if you remove a needed arm. The first change is to combine all the arms where the command is to close the gate; that's always allowed. Add the following code as the first arm in your switch expression: @@ -96,15 +96,15 @@ _ => throw new InvalidOperationException("Invalid internal state"), Run your tests again, and they pass. Here's the final version of the `SetHighGate` method: -:::code language="csharp" source="snippets/pattern-objects/CanalLock.cs" ID="FinalImplementation"::: +:::code language="csharp" source="snippets/patterns-objects/CanalLock.cs" ID="FinalImplementation"::: ## Implement patterns yourself -Now that you've seen the technique, fill in the `SetLowGate` and `SetWaterLevel` methods yourself. Start by adding the following code to test invalid operations on those methods: +Now that you've seen the technique, fill in the `SetLowGate` and `SetWaterLevel` methods yourself. Start by adding the following code to test invalid operations on those methods: -:::code language="csharp" source="snippets/pattern-objects/Program.cs" ID="FinalTestCode"::: +:::code language="csharp" source="snippets/patterns-objects/Program.cs" ID="FinalTestCode"::: -Run your application again. You can see the new tests fail, and the canal lock gets into an invalid state. Try to implement the remaining methods yourself. The method to set the lower gate should be similar to the method to set the upper gate. The method that changes the water level has different checks, but should follow a similar structure. You may find it helpful to use the same process for the method that sets the water level. Start with all four inputs: The state of both gates, the current state of the water level, and the requested new water level. The switch expression should start with: +Run your application again. You can see the new tests fail, and the canal lock gets into an invalid state. Try to implement the remaining methods yourself. The method to set the lower gate should be similar to the method to set the upper gate. The method that changes the water level has different checks, but should follow a similar structure. You might find it helpful to use the same process for the method that sets the water level. Start with all four inputs: The state of both gates, the current state of the water level, and the requested new water level. The switch expression should start with: ```csharp CanalLockWaterLevel = (newLevel, CanalLockWaterLevel, LowWaterGateOpen, HighWaterGateOpen) switch @@ -113,14 +113,14 @@ CanalLockWaterLevel = (newLevel, CanalLockWaterLevel, LowWaterGateOpen, HighWate }; ``` -You'll have 16 total switch arms to fill in. Then, test and simplify. +You have 16 total switch arms to fill in. Then, test and simplify. Did you make methods something like this? -:::code language="csharp" source="snippets/pattern-objects/CanalLock.cs" ID="FinalExercise"::: +:::code language="csharp" source="snippets/patterns-objects/CanalLock.cs" ID="FinalExercise"::: Your tests should pass, and the canal lock should operate safely. ## Summary -In this tutorial, you learned to use pattern matching to check the internal state of an object before applying any changes to that state. You can check combinations of properties. Once you've built tables for any of those transitions, you test your code, then simplify for readability and maintainability. These initial refactorings may suggest further refactorings that validate internal state or manage other API changes. This tutorial combined classes and objects with a more data-oriented, pattern-based approach to implement those classes. +In this tutorial, you learned to use pattern matching to check the internal state of an object before applying any changes to that state. You can check combinations of properties. Once you built tables for any of those transitions, you test your code, then simplify for readability and maintainability. These initial refactorings might suggest further refactorings that validate internal state or manage other API changes. This tutorial combined classes and objects with a more data-oriented, pattern-based approach to implement those classes. diff --git a/docs/csharp/tutorials/records.md b/docs/csharp/tutorials/records.md index 4747bdc83dfaf..c2777a6e2d85e 100644 --- a/docs/csharp/tutorials/records.md +++ b/docs/csharp/tutorials/records.md @@ -1,13 +1,13 @@ --- title: Use record types tutorial description: Learn about how to use record types, build hierarchies of records, and when to choose records over classes. -ms.date: 11/08/2023 +ms.date: 11/22/2024 --- # Create record types -[*Records*](../language-reference/builtin-types/record.md) are types that use *value-based equality*. C# 10 adds *record structs* so that you can define records as value types. Two variables of a record type are equal if the record type definitions are identical, and if for every field, the values in both records are equal. Two variables of a class type are equal if the objects referred to are the same class type and the variables refer to the same object. Value-based equality implies other capabilities you'll probably want in record types. The compiler generates many of those members when you declare a `record` instead of a `class`. The compiler generates those same methods for `record struct` types. +[*Records*](../language-reference/builtin-types/record.md) are types that use *value-based equality*. You can define records as reference types or value types. Two variables of a record type are equal if the record type definitions are identical, and if for every field, the values in both records are equal. Two variables of a class type are equal if the objects referred to are the same class type and the variables refer to the same object. Value-based equality implies other capabilities you probably want in record types. The compiler generates many of those members when you declare a `record` instead of a `class`. The compiler generates those same methods for `record struct` types. -In this tutorial, you'll learn how to: +In this tutorial, you learn how to: > [!div class="checklist"] > @@ -17,7 +17,7 @@ In this tutorial, you'll learn how to: ## Prerequisites -You'll need to set up your machine to run .NET 6 or later, including the C# 10 or later compiler. The C# 10 compiler is available starting with [Visual Studio 2022](https://visualstudio.microsoft.com/vs) or the [.NET 6 SDK](https://dotnet.microsoft.com/download). +You need to set up your machine to run .NET 6 or later. The C# compiler is available with [Visual Studio 2022](https://visualstudio.microsoft.com/vs) or the [.NET SDK](https://dotnet.microsoft.com/download). ## Characteristics of records @@ -29,7 +29,7 @@ You define a *record* by declaring a type with the `record` keyword, modifying a - Methods for `operator ==` and `operator !=`. - Record types implement . -Records also provide an override of . The compiler synthesizes methods for displaying records using . You'll explore those members as you write the code for this tutorial. Records support `with` expressions to enable non-destructive mutation of records. +Records also provide an override of . The compiler synthesizes methods for displaying records using . You explore those members as you write the code for this tutorial. Records support `with` expressions to enable nondestructive mutation of records. You can also declare *positional records* using a more concise syntax. The compiler synthesizes more methods for you when you declare positional records: @@ -39,7 +39,7 @@ You can also declare *positional records* using a more concise syntax. The compi ## Build temperature data -Data and statistics are among the scenarios where you'll want to use records. For this tutorial, you'll build an application that computes *degree days* for different uses. *Degree days* are a measure of heat (or lack of heat) over a period of days, weeks, or months. Degree days track and predict energy usage. More hotter days means more air conditioning, and more colder days means more furnace usage. Degree days help manage plant populations and correlate to plant growth as the seasons change. Degree days help track animal migrations for species that travel to match climate. +Data and statistics are among the scenarios where you want to use records. For this tutorial, you build an application that computes *degree days* for different uses. *Degree days* are a measure of heat (or lack of heat) over a period of days, weeks, or months. Degree days track and predict energy usage. More hotter days mean more air conditioning, and more colder days mean more furnace usage. Degree days help manage plant populations and correlate to plant growth as the seasons change. Degree days help track animal migrations for species that travel to match climate. The formula is based on the mean temperature on a given day and a baseline temperature. To compute degree days over time, you'll need the high and low temperature each day for a period of time. Let's start by creating a new application. Make a new console application. Create a new record type in a new file named "DailyTemperature.cs": @@ -49,7 +49,7 @@ The preceding code defines a *positional record*. The `DailyTemperature` record :::code language="csharp" source="snippets/record-types/Program.cs" ID="DeclareData"::: -You can add your own properties or methods to records, including positional records. You'll need to compute the mean temperature for each day. You can add that property to the `DailyTemperature` record: +You can add your own properties or methods to records, including positional records. You need to compute the mean temperature for each day. You can add that property to the `DailyTemperature` record: :::code language="csharp" source="snippets/record-types/DailyTemperature.cs" ID="TemperatureRecord"::: @@ -60,7 +60,7 @@ foreach (var item in data) Console.WriteLine(item); ``` -Run your application, and you'll see output that looks similar to the following display (several rows removed for space): +Run your application, and you see output that looks similar to the following display (several rows removed for space): ```dotnetcli DailyTemperature { HighTemp = 57, LowTemp = 30, Mean = 43.5 } @@ -75,7 +75,7 @@ The preceding code shows the output from the override of `ToString` synthesized ## Compute degree days -To compute degree days, you take the difference from a baseline temperature and the mean temperature on a given day. To measure heat over time, you discard any days where the mean temperature is below the baseline. To measure cold over time, you discard any days where the mean temperature is above the baseline. For example, the U.S. uses 65F as the base for both heating and cooling degree days. That's the temperature where no heating or cooling is needed. If a day has a mean temperature of 70F, that day is five cooling degree days and zero heating degree days. Conversely, if the mean temperature is 55F, that day is 10 heating degree days and 0 cooling degree days. +To compute degree days, you take the difference from a baseline temperature and the mean temperature on a given day. To measure heat over time, you discard any days where the mean temperature is below the baseline. To measure cold over time, you discard any days where the mean temperature is above the baseline. For example, the U.S. uses 65 F as the base for both heating and cooling degree days. That's the temperature where no heating or cooling is needed. If a day has a mean temperature of 70 F, that day is five cooling degree days and zero heating degree days. Conversely, if the mean temperature is 55 F, that day is 10 heating degree days and 0 cooling degree days. You can express these formulas as a small hierarchy of record types: an abstract degree day type and two concrete types for heating degree days and cooling degree days. These types can also be positional records. They take a baseline temperature and a sequence of daily temperature records as arguments to the primary constructor: @@ -85,7 +85,7 @@ The abstract `DegreeDays` record is the shared base class for both the `HeatingD :::code language="csharp" source="snippets/record-types/Program.cs" ID="HeatingAndCooling"::: -You'll get output like the following display: +You get output like the following display: ```dotnetcli HeatingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 85 } @@ -94,7 +94,7 @@ CoolingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemper ## Define compiler-synthesized methods -Your code calculates the correct number of heating and cooling degree days over that period of time. But this example shows why you may want to replace some of the synthesized methods for records. You can declare your own version of any of the compiler-synthesized methods in a record type except the clone method. The clone method has a compiler-generated name and you can't provide a different implementation. These synthesized methods include a copy constructor, the members of the interface, equality and inequality tests, and . For this purpose, you'll synthesize `PrintMembers`. You could also declare your own `ToString`, but `PrintMembers` provides a better option for inheritance scenarios. To provide your own version of a synthesized method, the signature must match the synthesized method. +Your code calculates the correct number of heating and cooling degree days over that period of time. But this example shows why you might want to replace some of the synthesized methods for records. You can declare your own version of any of the compiler-synthesized methods in a record type except the clone method. The clone method has a compiler-generated name and you can't provide a different implementation. These synthesized methods include a copy constructor, the members of the interface, equality and inequality tests, and . For this purpose, you synthesize `PrintMembers`. You could also declare your own `ToString`, but `PrintMembers` provides a better option for inheritance scenarios. To provide your own version of a synthesized method, the signature must match the synthesized method. The `TempRecords` element in the console output isn't useful. It displays the type, but nothing else. You can change this behavior by providing your own implementation of the synthesized `PrintMembers` method. The signature depends on modifiers applied to the `record` declaration: @@ -102,7 +102,7 @@ The `TempRecords` element in the console output isn't useful. It displays the ty - If a record type isn't `sealed` and derives from `object` (that is, it doesn't declare a base record), the signature is `protected virtual bool PrintMembers(StringBuilder builder);` - If a record type isn't `sealed` and derives from another record, the signature is `protected override bool PrintMembers(StringBuilder builder);` -These rules are easiest to comprehend through understanding the purpose of `PrintMembers`. `PrintMembers` adds information about each property in a record type to a string. The contract requires base records to add their members to the display and assumes derived members will add their members. Each record type synthesizes a `ToString` override that looks similar to the following example for `HeatingDegreeDays`: +These rules are easiest to comprehend through understanding the purpose of `PrintMembers`. `PrintMembers` adds information about each property in a record type to a string. The contract requires base records to add their members to the display and assumes derived members add their members. Each record type synthesizes a `ToString` override that looks similar to the following example for `HeatingDegreeDays`: ```csharp public override string ToString() @@ -125,15 +125,15 @@ You declare a `PrintMembers` method in the `DegreeDays` record that doesn't prin The signature declares a `virtual protected` method to match the compiler's version. Don't worry if you get the accessors wrong; the language enforces the correct signature. If you forget the correct modifiers for any synthesized method, the compiler issues warnings or errors that help you get the right signature. -In C# 10 and later, you can declare the `ToString` method as `sealed` in a record type. That prevents derived records from providing a new implementation. Derived records will still contain the `PrintMembers` override. You would seal `ToString` if you didn't want it to display the runtime type of the record. In the preceding example, you'd lose the information on where the record was measuring heating or cooling degree days. +You can declare the `ToString` method as `sealed` in a record type. That prevents derived records from providing a new implementation. Derived records will still contain the `PrintMembers` override. You would seal `ToString` if you didn't want it to display the runtime type of the record. In the preceding example, you'd lose the information on where the record was measuring heating or cooling degree days. -## Non-destructive mutation +## Nondestructive mutation The synthesized members in a positional record class don't modify the state of the record. The goal is that you can more easily create immutable records. Remember that you declare a `readonly record struct` to create an immutable record struct. Look again at the preceding declarations for `HeatingDegreeDays` and `CoolingDegreeDays`. The members added perform computations on the values for the record, but don't mutate state. Positional records make it easier for you to create immutable reference types. -Creating immutable reference types means you'll want to use non-destructive mutation. You create new record instances that are similar to existing record instances using [`with` expressions](../language-reference/operators/with-expression.md). These expressions are a copy construction with additional assignments that modify the copy. The result is a new record instance where each property has been copied from the existing record and optionally modified. The original record is unchanged. +Creating immutable reference types means you want to use nondestructive mutation. You create new record instances that are similar to existing record instances using [`with` expressions](../language-reference/operators/with-expression.md). These expressions are a copy construction with extra assignments that modify the copy. The result is a new record instance where each property was copied from the existing record and optionally modified. The original record is unchanged. -Let's add a couple features to your program that demonstrate `with` expressions. First, let's create a new record to compute growing degree days using the same data. *Growing degree days* typically uses 41F as the baseline and measures temperatures above the baseline. To use the same data, you can create a new record that is similar to the `coolingDegreeDays`, but with a different base temperature: +Let's add a couple features to your program that demonstrate `with` expressions. First, let's create a new record to compute growing degree days using the same data. *Growing degree days* typically uses 41 F as the baseline and measures temperatures above the baseline. To use the same data, you can create a new record that is similar to the `coolingDegreeDays`, but with a different base temperature: :::code language="csharp" source="snippets/record-types/Program.cs" ID="GrowingDegreeDays"::: @@ -151,7 +151,7 @@ Run the finished application to see the results. ## Summary -This tutorial showed several aspects of records. Records provide concise syntax for types where the fundamental use is storing data. For object-oriented classes, the fundamental use is defining responsibilities. This tutorial focused on *positional records*, where you can use a concise syntax to declare the properties for a record. The compiler synthesizes several members of the record for copying and comparing records. You can add any other members you need for your record types. You can create immutable record types knowing that none of the compiler-generated members would mutate state. And `with` expressions make it easy to support non-destructive mutation. +This tutorial showed several aspects of records. Records provide concise syntax for types where the fundamental use is storing data. For object-oriented classes, the fundamental use is defining responsibilities. This tutorial focused on *positional records*, where you can use a concise syntax to declare the properties for a record. The compiler synthesizes several members of the record for copying and comparing records. You can add any other members you need for your record types. You can create immutable record types knowing that none of the compiler-generated members would mutate state. And `with` expressions make it easy to support nondestructive mutation. Records add another way to define types. You use `class` definitions to create object-oriented hierarchies that focus on the responsibilities and behavior of objects. You create `struct` types for data structures that store data and are small enough to copy efficiently. You create `record` types when you want value-based equality and comparison, don't want to copy values, and want to use reference variables. You create `record struct` types when you want the features of records for a type that is small enough to copy efficiently. diff --git a/docs/csharp/whats-new/tutorials/snippets/pattern-objects/CanalLock.cs b/docs/csharp/tutorials/snippets/patterns-objects/CanalLock.cs similarity index 97% rename from docs/csharp/whats-new/tutorials/snippets/pattern-objects/CanalLock.cs rename to docs/csharp/tutorials/snippets/patterns-objects/CanalLock.cs index 5e9f648d72029..bbb506f7b8f21 100644 --- a/docs/csharp/whats-new/tutorials/snippets/pattern-objects/CanalLock.cs +++ b/docs/csharp/tutorials/snippets/patterns-objects/CanalLock.cs @@ -34,7 +34,7 @@ public void SetLowGate(bool open) { (false, _, _) => false, (true, _, WaterLevel.Low) => true, - (true, false, WaterLevel.High) => throw new InvalidOperationException("Cannot open high gate when the water is low"), + (true, false, WaterLevel.High) => throw new InvalidOperationException("Cannot open low gate when the water is high"), _ => throw new InvalidOperationException("Invalid internal state"), }; } diff --git a/docs/csharp/whats-new/tutorials/snippets/pattern-objects/InterimSteps.cs b/docs/csharp/tutorials/snippets/patterns-objects/InterimSteps.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/pattern-objects/InterimSteps.cs rename to docs/csharp/tutorials/snippets/patterns-objects/InterimSteps.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/pattern-objects/Program.cs b/docs/csharp/tutorials/snippets/patterns-objects/Program.cs similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/pattern-objects/Program.cs rename to docs/csharp/tutorials/snippets/patterns-objects/Program.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/pattern-objects/pattern-objects.csproj b/docs/csharp/tutorials/snippets/patterns-objects/pattern-objects.csproj similarity index 100% rename from docs/csharp/whats-new/tutorials/snippets/pattern-objects/pattern-objects.csproj rename to docs/csharp/tutorials/snippets/patterns-objects/pattern-objects.csproj diff --git a/docs/csharp/tutorials/top-level-statements.md b/docs/csharp/tutorials/top-level-statements.md index 347d8366d2fe4..a01cd945ab1a7 100644 --- a/docs/csharp/tutorials/top-level-statements.md +++ b/docs/csharp/tutorials/top-level-statements.md @@ -1,11 +1,11 @@ --- title: Top-level statements tutorial description: This tutorial shows how you can use top-level statements to experiment and prove concepts while exploring your ideas -ms.date: 11/14/2023 +ms.date: 11/22/2024 --- # Tutorial: Explore ideas using top-level statements to build code as you learn -In this tutorial, you'll learn how to: +In this tutorial, you learn how to: > [!div class="checklist"] > @@ -15,7 +15,7 @@ In this tutorial, you'll learn how to: ## Prerequisites -You'll need to set up your machine to run .NET 6, which includes the C# 10 compiler. The C# 10 compiler is available starting with [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) or [.NET 6 SDK](https://dot.net/get-dotnet6). +You need to set up your machine to run .NET 6 or later. The C# compiler is available starting with [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) or [.NET SDK](https://dot.net/get-dotnet6). This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or the .NET CLI. @@ -47,13 +47,13 @@ Console.WriteLine("Hello, World!"); [!INCLUDE [csharp10-templates](../../../includes/csharp10-templates.md)] -This feature simplifies what's needed to begin exploring new ideas. You can use top-level statements for scripting scenarios, or to explore. Once you've got the basics working, you can start refactoring the code and create methods, classes, or other assemblies for reusable components you've built. Top-level statements do enable quick experimentation and beginner tutorials. They also provide a smooth path from experimentation to full programs. +This feature simplifies what's needed to begin exploring new ideas. You can use top-level statements for scripting scenarios, or to explore. Once you've got the basics working, you can start refactoring the code and create methods, classes, or other assemblies for reusable components you built. Top-level statements do enable quick experimentation and beginner tutorials. They also provide a smooth path from experimentation to full programs. Top-level statements are executed in the order they appear in the file. Top-level statements can only be used in one source file in your application. The compiler generates an error if you use them in more than one file. ## Build a magic .NET answer machine -For this tutorial, let's build a console application that answers a "yes" or "no" question with a random answer. You'll build out the functionality step by step. You can focus on your task rather than ceremony needed for the structure of a typical program. Then, once you're happy with the functionality, you can refactor the application as you see fit. +For this tutorial, let's build a console application that answers a "yes" or "no" question with a random answer. You build out the functionality step by step. You can focus on your task rather than ceremony needed for the structure of a typical program. Then, once you're happy with the functionality, you can refactor the application as you see fit. A good starting point is to write the question back to the console. You can start by writing the following code: @@ -69,17 +69,17 @@ You can test your code by running the following `dotnet run` command: dotnet run -- Should I use top level statements in all my programs? ``` -The arguments after the `--` on the command line are passed to the program. You can see the type of the `args` variable, because that's what's printed to the console: +The arguments after the `--` on the command line are passed to the program. You can see the type of the `args` variable, because that's printed to the console: ```console System.String[] ``` -To write the question to the console, you'll need to enumerate the arguments and separate them with a space. Replace the `WriteLine` call with the following code: +To write the question to the console, you need to enumerate the arguments and separate them with a space. Replace the `WriteLine` call with the following code: :::code language="csharp" source="snippets/top-level-statements/ProgramSnippets.cs" ID="EchoInput"::: -Now, when you run the program, it will correctly display the question as a string of arguments. +Now, when you run the program, it correctly displays the question as a string of arguments. ## Respond with a random answer @@ -87,7 +87,7 @@ After echoing the question, you can add the code to generate the random answer. :::code language="csharp" source="snippets/top-level-statements/ProgramSnippets.cs" ID="Answers"::: -This array has ten answers that are affirmative, five that are non-committal, and five that are negative. Next, add the following code to generate and display a random answer from the array: +This array has 10 answers that are affirmative, five that are noncommittal, and five that are negative. Next, add the following code to generate and display a random answer from the array: :::code language="csharp" source="snippets/top-level-statements/ProgramSnippets.cs" ID="GenerateAnswer"::: @@ -100,11 +100,11 @@ Should I use top level statements in all my programs? Better not tell you now. ``` -This code answers the questions, but let's add one more feature. You'd like your question app to simulate thinking about the answer. You can do that by adding a bit of ASCII animation, and pausing while working. Add the following code after the line that echoes the question: +This code answers the questions, but let's add one more feature. You'd like your question app to simulate thinking about the answer. You can do that by adding a bit of ASCII animation, and pausing while working. Add the following code after the line that echoes the question: :::code language="csharp" source="snippets/top-level-statements/UtilitiesPassOne.cs" ID="AnimationFirstPass"::: -You'll also need to add a `using` directive to the top of the source file: +You also need to add a `using` directive to the top of the source file: ```csharp using System.Threading.Tasks; @@ -192,25 +192,25 @@ static async Task ShowConsoleAnimation() } ``` -The preceding code creates a local function inside your main method. That's still not reusable. So, extract that code into a class. Create a new file named *utilities.cs* and add the following code: +The preceding code creates a local function inside your main method. That code still isn't reusable. So, extract that code into a class. Create a new file named *utilities.cs* and add the following code: :::code language="csharp" source="snippets/top-level-statements/UtilitiesPassOne.cs" ID="SnippetUtilities"::: A file that has top-level statements can also contain namespaces and types at the end of the file, after the top-level statements. But for this tutorial you put the animation method in a separate file to make it more readily reusable. Finally, you can clean the animation code to remove some duplication, by using `foreach` loop to iterate through set of animations elements defined in `animations` array. -
The full `ShowConsoleAnimation` method after refactor should look similar to the following: +
The full `ShowConsoleAnimation` method after refactor should look similar to the following code: :::code language="csharp" source="snippets/top-level-statements/Utilities.cs" ID="Animation"::: -Now you have a complete application, and you've refactored the reusable parts for later use. You can call the new utility method from your top-level statements, as shown below in the finished version of the main program: +Now you have a complete application, and you refactored the reusable parts for later use. You can call the new utility method from your top-level statements, as shown in the finished version of the main program: :::code language="csharp" source="snippets/top-level-statements/Program.cs"::: -The preceding example adds the call to `Utilities.ShowConsoleAnimation`, and adds an additional `using` directive. +The preceding example adds the call to `Utilities.ShowConsoleAnimation`, and adds another `using` directive. ## Summary -Top-level statements make it easier to create simple programs for use to explore new algorithms. You can experiment with algorithms by trying different snippets of code. Once you've learned what works, you can refactor the code to be more maintainable. +Top-level statements make it easier to create simple programs for use to explore new algorithms. You can experiment with algorithms by trying different snippets of code. Once you learned what works, you can refactor the code to be more maintainable. -Top-level statements simplify programs that are based on console applications. These include Azure functions, GitHub actions, and other small utilities. For more information, see [Top-level statements (C# Programming Guide)](../fundamentals/program-structure/top-level-statements.md). +Top-level statements simplify programs that are based on console apps. These apps include Azure functions, GitHub actions, and other small utilities. For more information, see [Top-level statements (C# Programming Guide)](../fundamentals/program-structure/top-level-statements.md). diff --git a/docs/csharp/whats-new/csharp-10.md b/docs/csharp/whats-new/csharp-10.md deleted file mode 100644 index 50fa7e919f691..0000000000000 --- a/docs/csharp/whats-new/csharp-10.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: What's new in C# 10 -description: Get an overview of the new features available in C# 10. -ms.date: 11/08/2021 -ms.custom: UpdateFrequency1 -ms.topic: whats-new ---- -# What's new in C# 10 - -C# 10 adds the following features and enhancements to the C# language: - -- [Record structs](#record-structs) -- [Improvements of structure types](#improvements-of-structure-types) -- [Interpolated string handlers](#interpolated-string-handler) -- [`global using` directives](#global-using-directives) -- [File-scoped namespace declaration](#file-scoped-namespace-declaration) -- [Extended property patterns](#extended-property-patterns) -- [Improvements on lambda expressions](#lambda-expression-improvements) -- [Allow `const` interpolated strings](#constant-interpolated-strings) -- [Record types can seal `ToString()`](#record-types-can-seal-tostring) -- [Improved definite assignment](#improved-definite-assignment) -- [Allow both assignment and declaration in the same deconstruction](#assignment-and-declaration-in-same-deconstruction) -- [Allow `AsyncMethodBuilder` attribute on methods](#allow-asyncmethodbuilder-attribute-on-methods) -- [CallerArgumentExpression attribute](#callerargumentexpression-attribute-diagnostics) -- [Enhanced `#line` pragma](#enhanced-line-pragma) -- [Warning wave 6](../language-reference/compiler-messages/warning-waves.md#cs8826---partial-method-declarations-have-signature-differences) - -C# 10 is supported on **.NET 6**. For more information, see [C# language versioning](../language-reference/configure-language-version.md). - -You can download the latest .NET 6 SDK from the [.NET downloads page](https://dotnet.microsoft.com/download). You can also download [Visual Studio 2022](https://visualstudio.microsoft.com/vs/), which includes the .NET 6 SDK. - -[!INCLUDE [released-version-feedback](./includes/released-feedback.md)] - -## Record structs - -You can declare value type records using the [`record struct` or `readonly record struct` declarations](../language-reference/builtin-types/record.md). You can now clarify that a `record` is a reference type with the `record class` declaration. - -## Improvements of structure types - -C# 10 introduces the following improvements related to structure types: - -- You can declare an instance parameterless constructor in a structure type and initialize an instance field or property at its declaration. For more information, see the [Struct initialization and default values](../language-reference/builtin-types/struct.md#struct-initialization-and-default-values) section of the [Structure types](../language-reference/builtin-types/struct.md) article. -- A left-hand operand of the [`with` expression](../language-reference/operators/with-expression.md) can be of any structure type or an anonymous (reference) type. - -## Interpolated string handler - -You can create a type that builds the resulting string from an [interpolated string expression](../language-reference/tokens/interpolated.md#compilation-of-interpolated-strings). The .NET libraries use this feature in many APIs. You can build one by [following this tutorial](./tutorials/interpolated-string-handler.md). - -## Global using directives - -You can add the `global` modifier to any [using directive](../language-reference/keywords/using-directive.md) to instruct the compiler that the directive applies to all source files in the compilation. This is typically all source files in a project. - -## File-scoped namespace declaration - -You can use a new form of the [`namespace` declaration](../language-reference/keywords/namespace.md) to declare that all declarations that follow are members of the declared namespace: - -```csharp -namespace MyNamespace; -``` - -This new syntax saves both horizontal and vertical space for `namespace` declarations. - -## Extended property patterns - -Beginning with C# 10, you can reference nested properties or fields within a property pattern. For example, a pattern of the form - -```csharp -{ Prop1.Prop2: pattern } -``` - -is valid in C# 10 and later and equivalent to - -```csharp -{ Prop1: { Prop2: pattern } } -``` - -valid in C# 8.0 and later. - -For more information, see the [Extended property patterns](~/_csharplang/proposals/csharp-10.0/extended-property-patterns.md) feature proposal note. For more information about a property pattern, see the [Property pattern](../language-reference/operators/patterns.md#property-pattern) section of the [Patterns](../language-reference/operators/patterns.md) article. - -## Lambda expression improvements - -C# 10 includes many improvements to how lambda expressions are handled: - -- Lambda expressions may have a [natural type](../language-reference/operators/lambda-expressions.md#natural-type-of-a-lambda-expression), where the compiler can infer a delegate type from the lambda expression or method group. -- Lambda expressions may declare a [return type](../language-reference/operators/lambda-expressions.md#explicit-return-type) when the compiler can't infer it. -- [Attributes](../language-reference/operators/lambda-expressions.md#attributes) can be applied to lambda expressions. - -These features make lambda expressions more similar to methods and local functions. They make it easier to use lambda expressions without declaring a variable of a delegate type, and they work more seamlessly with the new ASP.NET Core Minimal APIs. - -## Constant interpolated strings - -In C# 10, `const` strings may be initialized using [string interpolation](../language-reference/tokens/interpolated.md) if all the placeholders are themselves constant strings. String interpolation can create more readable constant strings as you build constant strings used in your application. The placeholder expressions can't be numeric constants because those constants are converted to strings at run time. The current culture may affect their string representation. Learn more in the language reference on [`const` expressions](../language-reference/keywords/const.md). - -## Record types can seal ToString - -In C# 10, you can add the `sealed` modifier when you override `ToString` in a record type. Sealing the `ToString` method prevents the compiler from synthesizing a `ToString` method for any derived record types. A `sealed` `ToString` ensures all derived record types use the `ToString` method defined in a common base record type. You can learn more about this feature in the article on [records](../language-reference/builtin-types/record.md). - -## Assignment and declaration in same deconstruction - -This change removes a restriction from earlier versions of C#. Previously, a deconstruction could assign all values to existing variables, or initialize newly declared variables: - -```csharp -// Initialization: -(int x, int y) = point; - -// assignment: -int x1 = 0; -int y1 = 0; -(x1, y1) = point; -``` - -C# 10 removes this restriction: - -```csharp -int x = 0; -(x, int y) = point; -``` - -## Improved definite assignment - -Prior to C# 10, there were many scenarios where definite assignment and null-state analysis produced warnings that were false positives. These generally involved comparisons to boolean constants, accessing a variable only in the `true` or `false` statements in an `if` statement, and null coalescing expressions. These examples generated warnings in previous versions of C#, but don't in C# 10: - -```csharp -string representation = "N/A"; -if ((c != null && c.GetDependentValue(out object obj)) == true) -{ - representation = obj.ToString(); // undesired error -} - -// Or, using ?. -if (c?.GetDependentValue(out object obj) == true) -{ - representation = obj.ToString(); // undesired error -} - -// Or, using ?? -if (c?.GetDependentValue(out object obj) ?? false) -{ - representation = obj.ToString(); // undesired error -} -``` - -The main impact of this improvement is that the warnings for definite assignment and null-state analysis are more accurate. - -## Allow AsyncMethodBuilder attribute on methods - -In C# 10 and later, you can specify a different async method builder for a single method, in addition to specifying the method builder type for all methods that return a given task-like type. A custom async method builder enables advanced performance tuning scenarios where a given method may benefit from a custom builder. - -To learn more, see the section on [`AsyncMethodBuilder`](../language-reference/attributes/general.md#asyncmethodbuilder-attribute) in the article on attributes read by the compiler. - -## CallerArgumentExpression attribute diagnostics - -You can use the to specify a parameter that the compiler replaces with the text representation of another argument. This feature enables libraries to create more specific diagnostics. The following code tests a condition. If the condition is false, the exception message contains the text representation of the argument passed to `condition`: - -```csharp -public static void Validate(bool condition, [CallerArgumentExpression("condition")] string? message=null) -{ - if (!condition) - { - throw new InvalidOperationException($"Argument failed validation: <{message}>"); - } -} -``` - -You can learn more about this feature in the article on [Caller information attributes](../language-reference/attributes/caller-information.md#argument-expressions) in the language reference section. - -## Enhanced #line pragma - -C# 10 supports a new format for the `#line` pragma. You likely won't use the new format, but you'll see its effects. The enhancements enable more fine-grained output in domain-specific languages (DSLs) like Razor. The Razor engine uses these enhancements to improve the debugging experience. You'll find debuggers can highlight your Razor source more accurately. To learn more about the new syntax, see the article on [Preprocessor directives](../language-reference/preprocessor-directives.md#error-and-warning-information) in the language reference. You can also read the [feature specification](~/_csharplang/proposals/csharp-10.0/enhanced-line-directives.md#examples) for Razor based examples. diff --git a/docs/csharp/whats-new/csharp-version-history.md b/docs/csharp/whats-new/csharp-version-history.md index 862053352cd95..193815804c5df 100644 --- a/docs/csharp/whats-new/csharp-version-history.md +++ b/docs/csharp/whats-new/csharp-version-history.md @@ -2,7 +2,7 @@ title: The history of C# description: Learn how the C# language has changed over its many releases. Learn when different features were introduced in the language. author: erikdietrich -ms.date: 03/06/2024 +ms.date: 11/22/2024 ms.custom: "updateeachrelease, UpdateFrequency1" --- @@ -61,20 +61,22 @@ C# 11 introduces *generic math* and several features that support that goal. You C# 10 adds the following features and enhancements to the C# language: -- [Record structs](./csharp-10.md#record-structs) -- [Improvements of structure types](./csharp-10.md#improvements-of-structure-types) -- [Interpolated string handlers](./csharp-10.md#interpolated-string-handler) -- [`global using` directives](./csharp-10.md#global-using-directives) -- [File-scoped namespace declaration](./csharp-10.md#file-scoped-namespace-declaration) -- [Extended property patterns](./csharp-10.md#extended-property-patterns) -- [Improvements on lambda expressions](./csharp-10.md#lambda-expression-improvements) -- [Allow `const` interpolated strings](./csharp-10.md#constant-interpolated-strings) -- [Record types can seal `ToString()`](./csharp-10.md#record-types-can-seal-tostring) -- [Improved definite assignment](./csharp-10.md#improved-definite-assignment) -- [Allow both assignment and declaration in the same deconstruction](./csharp-10.md#assignment-and-declaration-in-same-deconstruction) -- [Allow `AsyncMethodBuilder` attribute on methods](./csharp-10.md#allow-asyncmethodbuilder-attribute-on-methods) -- [CallerArgumentExpression attribute](./csharp-10.md#callerargumentexpression-attribute-diagnostics) -- [Enhanced `#line` pragma](./csharp-10.md#enhanced-line-pragma) +- [Record structs](../language-reference/builtin-types/record.md) +- [Improvements of structure types](../language-reference/builtin-types/struct.md#struct-initialization-and-default-values) +- [Interpolated string handlers](../language-reference/tokens/interpolated.md#compilation-of-interpolated-strings) +- [`global using` directives](../language-reference/keywords/using-directive.md) +- [File-scoped namespace declaration](../language-reference/keywords/namespace.md) +- [Extended property patterns](~/_csharplang/proposals/csharp-10.0/extended-property-patterns.md) +- Lambda expressions can have a [natural type](../language-reference/operators/lambda-expressions.md#natural-type-of-a-lambda-expression), where the compiler can infer a delegate type from the lambda expression or method group. +- Lambda expressions can declare a [return type](../language-reference/operators/lambda-expressions.md#explicit-return-type) when the compiler can't infer it. +- [Attributes](../language-reference/operators/lambda-expressions.md#attributes) can be applied to lambda expressions. +- In C# 10, `const` strings can be initialized using [string interpolation](../language-reference/tokens/interpolated.md) if all the placeholders are themselves constant strings. +- In C# 10, you can add the `sealed` modifier when you override `ToString` in a [record](../language-reference/builtin-types/record.md) type. +- Warnings for definite assignment and null-state analysis are more accurate. +- Allow both assignment and declaration in the same deconstruction. +- [Allow `AsyncMethodBuilder` attribute on methods](../language-reference/attributes/general.md#asyncmethodbuilder-attribute) +- [CallerArgumentExpression attribute](../language-reference/attributes/caller-information.md#argument-expressions) +- C# 10 supports a new format for the `#line` pragma. More features were available in *preview* mode. In order to use these features, you must [set `` to `Preview`](../language-reference/compiler-options/language.md#langversion) in your project: @@ -131,15 +133,7 @@ C# 9 includes new pattern matching improvements: - ***Negated `not` patterns*** require that a pattern doesn't match - ***Relational patterns*** require the input be less than, greater than, less than or equal, or greater than or equal to a given constant -These patterns enrich the syntax for patterns. Consider these examples: - -:::code language="csharp" source="snippets/whats-new-csharp9/PatternUtilities.cs" ID="IsLetterPattern"::: - -With optional parentheses to make it clear that `and` has higher precedence than `or`: - -:::code language="csharp" source="snippets/whats-new-csharp9/PatternUtilities.cs" ID="IsLetterOrSeparatorPattern"::: - -One of the most common uses is a new syntax for a null check: +These patterns enrich the syntax for patterns. One of the most common uses is a new syntax for a null check: ```csharp if (e is not null) @@ -243,7 +237,7 @@ C# 7.2 added several small language features: - Add the `in` modifier on parameters, to specify that an argument is passed by reference but not modified by the called method. - Use the `ref readonly` modifier on method returns, to indicate that a method returns its value by reference but doesn't allow writes to that object. - Declare `ref struct` types, to indicate that a struct type accesses managed memory directly and must always be stack allocated. -- Use additional generic constraints. +- Use more generic constraints. - [Non-trailing named arguments](../programming-guide/classes-and-structs/named-and-optional-arguments.md): - Positional arguments can follow named arguments. - Leading underscores in numeric literals: diff --git a/docs/csharp/whats-new/snippets/whats-new-csharp9/NewCsharp9.csproj b/docs/csharp/whats-new/snippets/whats-new-csharp9/NewCsharp9.csproj deleted file mode 100644 index f704bf4988fa6..0000000000000 --- a/docs/csharp/whats-new/snippets/whats-new-csharp9/NewCsharp9.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - diff --git a/docs/csharp/whats-new/snippets/whats-new-csharp9/PatternUtilities.cs b/docs/csharp/whats-new/snippets/whats-new-csharp9/PatternUtilities.cs deleted file mode 100644 index 274e1a1143a4a..0000000000000 --- a/docs/csharp/whats-new/snippets/whats-new-csharp9/PatternUtilities.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace NewCsharp9 -{ - public static class PatternUtilities - { - // - public static bool IsLetter(this char c) => - c is >= 'a' and <= 'z' or >= 'A' and <= 'Z'; - // - - // - public static bool IsLetterOrSeparator(this char c) => - c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ','; - // - - } -} diff --git a/docs/csharp/whats-new/snippets/whats-new-csharp9/Program.cs b/docs/csharp/whats-new/snippets/whats-new-csharp9/Program.cs deleted file mode 100644 index 505664b3da34f..0000000000000 --- a/docs/csharp/whats-new/snippets/whats-new-csharp9/Program.cs +++ /dev/null @@ -1,7 +0,0 @@ -using NewCsharp9; - -// -using System; - -Console.WriteLine("Hello World!"); -// diff --git a/docs/fundamentals/code-analysis/overview.md b/docs/fundamentals/code-analysis/overview.md index 1b7b2be89e6b6..07a3349700bbd 100644 --- a/docs/fundamentals/code-analysis/overview.md +++ b/docs/fundamentals/code-analysis/overview.md @@ -25,7 +25,7 @@ If rule violations are found by an analyzer, they're reported as a suggestion, w *Code quality analysis* ("CAxxxx") rules inspect your C# or Visual Basic code for security, performance, design and other issues. Analysis is enabled, by default, for projects that target .NET 5 or later. You can enable code analysis on projects that target earlier .NET versions by setting the [EnableNETAnalyzers](../../core/project-sdk/msbuild-props.md#enablenetanalyzers) property to `true`. You can also disable code analysis for your project by setting `EnableNETAnalyzers` to `false`. > [!TIP] -> If you're using Visual Studio, many analyzer rules have associated *code fixes* that you can apply to correct the problem. Code fixes are shown in the light bulb icon menu. +> If you're using Visual Studio, many analyzer rules have associated *code fixes* that you can apply to automatically correct the problem. Code fixes are shown in the light bulb icon menu. ### Enabled rules diff --git a/docs/fundamentals/code-analysis/quality-rules/ca1865-ca1867.md b/docs/fundamentals/code-analysis/quality-rules/ca1865-ca1867.md index 83cb44ca9b477..5ebff0edd5b1b 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca1865-ca1867.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca1865-ca1867.md @@ -39,11 +39,11 @@ The target methods on `string` for these rules: The following table summarizes the conditions for each of the related rule IDs. -| Diagnostic ID | Description | Code fix available | -| - | - | - | -| CA1865 | Applies when a safe transformation can be performed automatically with a code fix. | Yes | -| CA1866 | Applies when there's no specified comparison. | No | -| CA1867 | Applies for any other string comparison not covered by the other two rules. | No | +| Diagnostic ID | Description | Code fix available? | +|---------------|------------------------------------------------------------------------------------|---------------------| +| CA1865 | Applies when a safe transformation can be performed automatically with a code fix. | Yes | +| CA1866 | Applies when there's no specified comparison. | No | +| CA1867 | Applies for any other string comparison not covered by the other two rules. | No | CA1867 is disabled by default. diff --git a/docs/fundamentals/code-analysis/quality-rules/ca1870.md b/docs/fundamentals/code-analysis/quality-rules/ca1870.md index de176fbb4fdc1..23073690de7b1 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca1870.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca1870.md @@ -37,7 +37,7 @@ Using a cached instance is more efficient Create and cache a instance in a `static readonly` field, then pass that instance to the `IndexOfAny` or `ContainsAny` call instead. -A code fixer that performs this transformation is available. +A *code fix* that automatically performs this transformation is available. ## Example diff --git a/docs/fundamentals/code-analysis/style-rules/ide0028.md b/docs/fundamentals/code-analysis/style-rules/ide0028.md index caeb5f3ba5f87..b5aab6971bbe2 100644 --- a/docs/fundamentals/code-analysis/style-rules/ide0028.md +++ b/docs/fundamentals/code-analysis/style-rules/ide0028.md @@ -28,10 +28,10 @@ dev_langs: This style rule concerns the use of [collection initializers](../../../csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md) and, if you're using C# 12 or later, [collection expressions](../../../csharp/language-reference/operators/collection-expressions.md) for collection initialization. -In .NET 8 (C# 12) and later versions, if you have the `dotnet_style_prefer_collection_expression` option set to `true`, the code fixer in Visual Studio converts your collection initialization code to use a collection expression (`List list = [1, 2, 3];`). In Visual Basic and in .NET 7 (C# 11) and earlier versions, the code fixer converts your code to use a collection initializer (`List list = new List { 1, 2, 3 };`). +In .NET 8 (C# 12) and later versions, if you have the `dotnet_style_prefer_collection_expression` option set to `true`, the *code fix* in Visual Studio converts your collection initialization code to use a collection expression (`List list = [1, 2, 3];`). In Visual Basic and in .NET 7 (C# 11) and earlier versions, the *code fix* converts your code to use a collection initializer (`List list = new List { 1, 2, 3 };`). > [!NOTE] -> If you use the code fixer in Visual Studio, the change it offers might have different semantics in some cases. For example, `int[] x = new int[] { }` is replaced with `int[] x = [];`, which has slightly different semantics—the compiler uses a singleton for `x` instead of creating a new instance. +> If you use the *code fix* in Visual Studio, the change it offers might have different semantics in some cases. For example, `int[] x = new int[] { }` is replaced with `int[] x = [];`, which has slightly different semantics—the compiler uses a singleton for `x` instead of creating a new instance. ## Options diff --git a/docs/fundamentals/code-analysis/style-rules/ide0058.md b/docs/fundamentals/code-analysis/style-rules/ide0058.md index 5f308b14adf43..3f0bafa69cbed 100644 --- a/docs/fundamentals/code-analysis/style-rules/ide0058.md +++ b/docs/fundamentals/code-analysis/style-rules/ide0058.md @@ -62,13 +62,13 @@ For information about configuring options, see [Option format](language-rules.md ### csharp_style_unused_value_expression_statement_preference -| Property | Value | Description | -| ------------------------ | --------------------------------------------------------- | --------------------------------------------------------------------------- | -| **Option name** | csharp_style_unused_value_expression_statement_preference | | -| **Applicable languages** | C# | | -| **Option values** | `discard_variable` | Prefer to assign an unused expression to a discard | +| Property | Value | Description | +|--------------------------|-----------------------------------------------------------|-------------| +| **Option name** | csharp_style_unused_value_expression_statement_preference | | +| **Applicable languages** | C# | | +| **Option values** | `discard_variable` | Prefer to assign an unused expression to a discard | | | `unused_local_variable` | Prefer to assign an unused expression to a local variable that's never used | -| **Default option value** | `discard_variable` | | +| **Default option value** | `discard_variable` | | ```csharp // Original code: @@ -85,12 +85,12 @@ var unused = Convert.ToInt32("35"); ### visual_basic_style_unused_value_expression_statement_preference -| Property | Value | Description | -| ------------------------ | --------------------------------------------------------------- | --------------------------------------------------------------------------- | -| **Option name** | visual_basic_style_unused_value_expression_statement_preference | | -| **Applicable languages** | Visual Basic | | +| Property | Value | Description | +|--------------------------|-----------------------------------------------------------------|-------------| +| **Option name** | visual_basic_style_unused_value_expression_statement_preference | | +| **Applicable languages** | Visual Basic | | | **Option values** | `unused_local_variable` | Prefer to assign an unused expression to a local variable that's never used | -| **Default option value** | `unused_local_variable` | | +| **Default option value** | `unused_local_variable` | | ```vb ' visual_basic_style_unused_value_expression_statement_preference = unused_local_variable diff --git a/docs/fundamentals/toc.yml b/docs/fundamentals/toc.yml index f89a709e279d5..2e2b40b0ea8a0 100644 --- a/docs/fundamentals/toc.yml +++ b/docs/fundamentals/toc.yml @@ -769,8 +769,8 @@ items: href: ../standard/serialization/system-text-json/use-utf8jsonreader.md - name: Visual Basic support href: ../standard/serialization/system-text-json/visual-basic-support.md - - name: Supported collection types - href: ../standard/serialization/system-text-json/supported-collection-types.md + - name: Supported types + href: ../standard/serialization/system-text-json/supported-types.md - name: Advanced items: - name: Source generation diff --git a/docs/navigate/tools-diagnostics/toc.yml b/docs/navigate/tools-diagnostics/toc.yml index 91f27ebffda75..7a7ed0db9b00a 100644 --- a/docs/navigate/tools-diagnostics/toc.yml +++ b/docs/navigate/tools-diagnostics/toc.yml @@ -471,6 +471,8 @@ items: href: ../../core/diagnostics/dumps.md - name: Linux dumps href: ../../core/diagnostics/debug-linux-dumps.md + - name: .NET debugger extensions + href: ../../core/diagnostics/debugger-extensions.md - name: SOS debugger extension href: ../../core/diagnostics/sos-debugging-extension.md - name: Debug Windows dumps @@ -524,6 +526,8 @@ items: href: ../../core/diagnostics/dotnet-stack.md - name: dotnet-symbol href: ../../core/diagnostics/dotnet-symbol.md + - name: dotnet-debugger-extensions + href: ../../core/diagnostics/dotnet-debugger-extensions.md - name: dotnet-sos href: ../../core/diagnostics/dotnet-sos.md - name: dotnet-dsrouter diff --git a/docs/standard/data/sqlite/connection-strings.md b/docs/standard/data/sqlite/connection-strings.md index d83b3c93295f9..976a9e2674e6e 100644 --- a/docs/standard/data/sqlite/connection-strings.md +++ b/docs/standard/data/sqlite/connection-strings.md @@ -112,8 +112,6 @@ You can use as a stro [!code-csharp[](../../../../samples/snippets/standard/data/sqlite/EncryptionSample/Program.cs?name=snippet_ConnectionStringBuilder)] -[!INCLUDE [managed-identities](../../../includes/managed-identities.md)] - ## Examples ### Basic diff --git a/docs/standard/serialization/binaryformatter-migration-guide/migrate-to-system-text-json.md b/docs/standard/serialization/binaryformatter-migration-guide/migrate-to-system-text-json.md index 84ffd656f4b06..e350f349dca54 100644 --- a/docs/standard/serialization/binaryformatter-migration-guide/migrate-to-system-text-json.md +++ b/docs/standard/serialization/binaryformatter-migration-guide/migrate-to-system-text-json.md @@ -34,9 +34,9 @@ When migrating from BinaryFormatter to `System.Text.Json`, it's crucial to note - To override the default serialization behavior for a specific type, you can [write custom converters](../system-text-json/converters-how-to.md). -- It supports serialization and deserialization of many collections, but there are limitations. See the [supported collection types](../system-text-json/supported-collection-types.md) documentation for details on which collections are supported for serialization and deserialization. +- It supports serialization and deserialization of many collections, but there are limitations. See the [supported types](../system-text-json/supported-types.md) documentation for details on which types and collections are supported for serialization and deserialization. -- Under [certain conditions](../system-text-json/supported-collection-types.md#custom-collections-with-deserialization-support), it supports serialization and deserialization of custom generic collections. +- Under [certain conditions](../system-text-json/supported-types.md#deserialization-support), it supports serialization and deserialization of custom generic collections. - Other types [without built-in support](../system-text-json/migrate-from-newtonsoft.md#types-without-built-in-support) are: `DataSet`, `DataTable`, `DBNull`, `TimeZoneInfo`, `Type`, `ValueTuple`. However, you can write a custom converter to support these types. diff --git a/docs/standard/serialization/system-text-json/converters-how-to.md b/docs/standard/serialization/system-text-json/converters-how-to.md index 3c62bb52f4858..693788f5e79c4 100644 --- a/docs/standard/serialization/system-text-json/converters-how-to.md +++ b/docs/standard/serialization/system-text-json/converters-how-to.md @@ -206,7 +206,7 @@ The following sections provide converter samples that address some common scenar * [Support round trip for `Stack` types](#support-round-trip-for-stack-types). * [Use default system converter](#use-default-system-converter). -For a sample converter, see [Supported collection types](supported-collection-types.md#systemdata-namespace). +For a sample converter, see [Supported types](supported-types.md#systemdata-namespace). ### Deserialize inferred types to object properties diff --git a/docs/standard/serialization/system-text-json/how-to.md b/docs/standard/serialization/system-text-json/how-to.md index 90ef05e1cc755..b599d90ff7ba5 100644 --- a/docs/standard/serialization/system-text-json/how-to.md +++ b/docs/standard/serialization/system-text-json/how-to.md @@ -77,7 +77,7 @@ Supported types include: * * - For more information, see [Supported collection types in System.Text.Json](supported-collection-types.md). + For more information, see [Supported types in System.Text.Json](supported-types.md). You can [implement custom converters](converters-how-to.md) to handle additional types or to provide functionality that isn't supported by the built-in converters. diff --git a/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md b/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md index 730acd1e57f09..25bf0ad548623 100644 --- a/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md +++ b/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md @@ -257,7 +257,7 @@ For more information, see [Preserve references and handle circular references](p ### Dictionary with non-string key -Both `Newtonsoft.Json` and `System.Text.Json` support collections of type `Dictionary`. However, in `System.Text.Json`, `TKey` must be a primitive type, not a custom type. For more information, see [Supported key types](supported-collection-types.md#supported-key-types). +Both `Newtonsoft.Json` and `System.Text.Json` support collections of type `Dictionary`. For information about supported key types, see [Supported key types](supported-types.md#supported-key-types). > [!CAUTION] > Deserializing to a `Dictionary` where `TKey` is typed as anything other than `string` could introduce a security vulnerability in the consuming application. For more information, see [dotnet/runtime#4761](https://github.com/dotnet/runtime/issues/4761). @@ -266,7 +266,7 @@ Both `Newtonsoft.Json` and `System.Text.Json` support collections of type `Dicti doesn't provide built-in support for the following types: -* and related types (for more information, see [Supported collection types](supported-collection-types.md#systemdata-namespace)) +* and related types (for more information, see [Supported types](supported-types.md#systemdata-namespace)) * * * diff --git a/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/Program.cs b/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/Program.cs deleted file mode 100644 index f4dd4609dda1b..0000000000000 --- a/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; - -namespace SystemTextJsonSamples -{ - public class Program - { - static async Task Main() - { - Console.WriteLine("\n============================= Serialize\n"); - await IAsyncEnumerableSerialize.Program.Main(); - Console.WriteLine("\n============================= Deserialize\n"); - await IAsyncEnumerableDeserialize.Program.Main(); - Console.WriteLine("\n============================= DeserializeNonStreaming\n"); - await IAsyncEnumerableDeserializeNonStreaming.Program.Main(); - } - } -} diff --git a/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/IAsyncEnumerableDeserialize.cs b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/IAsyncEnumerableDeserialize.cs similarity index 100% rename from docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/IAsyncEnumerableDeserialize.cs rename to docs/standard/serialization/system-text-json/snippets/supported-types/csharp/IAsyncEnumerableDeserialize.cs diff --git a/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/IAsyncEnumerableDeserializeNonStreaming.cs b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/IAsyncEnumerableDeserializeNonStreaming.cs similarity index 100% rename from docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/IAsyncEnumerableDeserializeNonStreaming.cs rename to docs/standard/serialization/system-text-json/snippets/supported-types/csharp/IAsyncEnumerableDeserializeNonStreaming.cs diff --git a/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/IAsyncEnumerableSerialize.cs b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/IAsyncEnumerableSerialize.cs similarity index 100% rename from docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/IAsyncEnumerableSerialize.cs rename to docs/standard/serialization/system-text-json/snippets/supported-types/csharp/IAsyncEnumerableSerialize.cs diff --git a/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/Program.cs b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/Program.cs new file mode 100644 index 0000000000000..a815dfd20ec9c --- /dev/null +++ b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/Program.cs @@ -0,0 +1,17 @@ +namespace SystemTextJsonSamples +{ + public class Program + { + static async Task Main() + { + //Console.WriteLine("\n============================= Serialize\n"); + //await IAsyncEnumerableSerialize.Program.Main(); + //Console.WriteLine("\n============================= Deserialize\n"); + //await IAsyncEnumerableDeserialize.Program.Main(); + //Console.WriteLine("\n============================= DeserializeNonStreaming\n"); + //await IAsyncEnumerableDeserializeNonStreaming.Program.Main(); + + StructSerialize.Program.Main(); + } + } +} diff --git a/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/Struct.cs b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/Struct.cs new file mode 100644 index 0000000000000..6219efd4de3be --- /dev/null +++ b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/Struct.cs @@ -0,0 +1,31 @@ +using System.Text; +using System.Text.Json; + +namespace StructSerialize; + +public class Program +{ + // + public static void Main() + { + var coordinates = new Coords(1.0, 2.0); + string json = JsonSerializer.Serialize(coordinates); + Console.WriteLine(json); + + // Output: + // {"X":1,"Y":2} + } + + public readonly struct Coords + { + public Coords(double x, double y) + { + X = x; + Y = y; + } + + public double X { get; } + public double Y { get; } + } + // +} diff --git a/docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/SystemTextJsonSamples.csproj b/docs/standard/serialization/system-text-json/snippets/supported-types/csharp/SystemTextJsonSamples.csproj similarity index 100% rename from docs/standard/serialization/system-text-json/snippets/supported-collection-types/csharp/SystemTextJsonSamples.csproj rename to docs/standard/serialization/system-text-json/snippets/supported-types/csharp/SystemTextJsonSamples.csproj diff --git a/docs/standard/serialization/system-text-json/supported-collection-types.md b/docs/standard/serialization/system-text-json/supported-types.md similarity index 76% rename from docs/standard/serialization/system-text-json/supported-collection-types.md rename to docs/standard/serialization/system-text-json/supported-types.md index 237e7c7567daf..ee11a96659ad8 100644 --- a/docs/standard/serialization/system-text-json/supported-collection-types.md +++ b/docs/standard/serialization/system-text-json/supported-types.md @@ -1,21 +1,35 @@ --- -title: "Supported collection types in System.Text.Json" -description: "Learn which collection types are supported for serialization by the APIs in the System.Text.Json namespace." -ms.date: 02/01/2021 +title: "Supported types in System.Text.Json" +description: "Learn which types are supported for serialization by the APIs in the System.Text.Json namespace." +ms.date: 11/25/2024 no-loc: [System.Text.Json] ms.topic: reference -helpviewer_keywords: - - "JSON serialization" - - "serializing objects" - - "serialization" - - "objects, serializing" --- -# Supported collection types in System.Text.Json +# Supported types in System.Text.Json -This article gives an overview of which collections are supported for serialization and deserialization. supports a collection type for serialization if it: +This article gives an overview of which types are supported for serialization and deserialization. -* Derives from or +## Types that serialize as JSON objects + +The following types serialize as JSON objects: + +* Classes* +* Structs +* Interfaces +* Records and struct records + +\* Non-dictionary types that implement serialize as JSON arrays. Dictionary types, which do implement , serialize as JSON objects. + +The following code snippet shows the serialization of a simple struct. + +:::code language="csharp" source="snippets/supported-types/csharp/Struct.cs" id="SerializeStruct"::: + +## Types that serialize as JSON arrays + +.NET collection types serialize as JSON arrays. supports a collection type for serialization if it: + +* Derives from or . * Contains elements that are serializable. The serializer calls the method and writes the elements. @@ -24,7 +38,16 @@ Deserialization is more complicated and is not supported for some collection typ The following sections are organized by namespace and show which types are supported for serialization and deserialization. -## System.Array namespace +* [System.Array namespace](#systemarray-namespace) +* [System.Collections namespace](#systemcollections-namespace) +* [System.Collections.Generic namespace](#systemcollectionsgeneric-namespace) +* [System.Collections.Immutable namespace](#systemcollectionsimmutable-namespace) +* [System.Collections.Specialized namespace](#systemcollectionsspecialized-namespace) +* [System.Collections.Concurrent namespace](#systemcollectionsconcurrent-namespace) +* [System.Collections.ObjectModel namespace](#systemcollectionsobjectmodel-namespace) +* [Custom collections](#custom-collections) + +### System.Array namespace | Type | Serialization | Deserialization | |-------------------------------------------------------------------------------------------------|---------------|-----------------| @@ -32,7 +55,7 @@ The following sections are organized by namespace and show which types are suppo | [Multi-dimensional arrays](../../../csharp/language-reference/builtin-types/arrays.md#multidimensional-arrays) | ❌ | ❌ | | [Jagged arrays](../../../csharp/language-reference/builtin-types/arrays.md#jagged-arrays) | ✔️ | ✔️ | -## System.Collections namespace +### System.Collections namespace | Type | Serialization | Deserialization | |-------------------------------------------|---------------|-----------------| @@ -46,19 +69,19 @@ The following sections are organized by namespace and show which types are suppo | | ✔️ | ✔️ | | | ✔️ | ✔️ | | | ✔️ | ✔️ | -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | \* See [Support round trip for `Stack` types](converters-how-to.md#support-round-trip-for-stack-types). -## System.Collections.Generic namespace +### System.Collections.Generic namespace | Type | Serialization | Deserialization | |-----------------------------------------------------------|---------------|-----------------| -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | | | ✔️ | ✔️ | | † | ✔️ | ✔️ | | | ✔️ | ✔️ | -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | | | ✔️ | ✔️ | | | ✔️ | ✔️ | | | ✔️ | ✔️ | @@ -71,7 +94,7 @@ The following sections are organized by namespace and show which types are suppo | | ✔️ | ✔️ | | | ✔️ | ✔️ | | \* | ✔️ | ✔️ | -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | | | ✔️ | ✔️ | | ‡ | ✔️ | ✔️ | @@ -81,33 +104,33 @@ The following sections are organized by namespace and show which types are suppo ‡ See [Support round trip for `Stack` types](converters-how-to.md#support-round-trip-for-stack-types). -### IAsyncEnumerable\ +#### IAsyncEnumerable\ The following examples use streams as a representation of any async source of data. The source could be files on a local machine, or results from a database query or web service API call. -#### Stream serialization +##### Stream serialization `System.Text.Json` supports serializing values as JSON arrays, as shown in the following example: -:::code language="csharp" source="snippets/supported-collection-types/csharp/IAsyncEnumerableSerialize.cs" highlight="15"::: +:::code language="csharp" source="snippets/supported-types/csharp/IAsyncEnumerableSerialize.cs" highlight="15"::: `IAsyncEnumerable` values are only supported by the asynchronous serialization methods, such as . -#### Stream deserialization +##### Stream deserialization The `DeserializeAsyncEnumerable` method supports streaming deserialization, as shown in the following example: -:::code language="csharp" source="snippets/supported-collection-types/csharp/IAsyncEnumerableDeserialize.cs" highlight="11"::: +:::code language="csharp" source="snippets/supported-types/csharp/IAsyncEnumerableDeserialize.cs" highlight="11"::: The `DeserializeAsyncEnumerable` method only supports reading from root-level JSON arrays. The method supports `IAsyncEnumerable`, but its signature doesn't allow streaming. It returns the final result as a single value, as shown in the following example. -:::code language="csharp" source="snippets/supported-collection-types/csharp/IAsyncEnumerableDeserializeNonStreaming.cs" highlight="16"::: +:::code language="csharp" source="snippets/supported-types/csharp/IAsyncEnumerableDeserializeNonStreaming.cs" highlight="16"::: In this example, the deserializer buffers all `IAsyncEnumerable` contents in memory before returning the deserialized object. This behavior is necessary because the deserializer needs to read the entire JSON payload before returning a result. -## System.Collections.Immutable namespace +### System.Collections.Immutable namespace | Type | Serialization | Deserialization | |-------------------------------------------------------------------|---------------|-----------------| @@ -115,24 +138,24 @@ In this example, the deserializer buffers all `IAsyncEnumerable` contents in | | ✔️ | ✔️ | | | ✔️ | ✔️ | | | ✔️ | ✔️ | -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | | | ✔️ | ✔️ | | † | ✔️ | ✔️ | | | ✔️ | ✔️ | | | ✔️ | ✔️ | | † | ✔️ | ✔️ | | | ✔️ | ✔️ | -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | \* See [Support round trip for `Stack` types](converters-how-to.md#support-round-trip-for-stack-types). † See [Supported key types](#supported-key-types). -## System.Collections.Specialized namespace +### System.Collections.Specialized namespace | Type | Serialization | Deserialization | |-----------------------------------------------------------|---------------|-----------------| -| | ✔️ | ❌\* | +| | ✔️ | ❌\* | | | ✔️ | ✔️ | | | ✔️ | ❌ | | | ✔️ | ✔️ | @@ -142,7 +165,7 @@ In this example, the deserializer buffers all `IAsyncEnumerable` contents in \* When is deserialized, the property is skipped because it doesn't have a public setter. No exception is thrown. -## System.Collections.Concurrent namespace +### System.Collections.Concurrent namespace | Type | Serialization | Deserialization | |---------------------------------------------------------------|---------------|-----------------| @@ -150,13 +173,13 @@ In this example, the deserializer buffers all `IAsyncEnumerable` contents in | | ✔️ | ❌ | | † | ✔️ | ✔️ | | | ✔️ | ✔️ | -| \* | ✔️ | ✔️ | +| \* | ✔️ | ✔️ | \* See [Support round trip for `Stack` types](converters-how-to.md#support-round-trip-for-stack-types). † See [Supported key types](#supported-key-types). -## System.Collections.ObjectModel namespace +### System.Collections.ObjectModel namespace | Type | Serialization | Deserialization | |----------------------------------------------------------------|---------------|-----------------| @@ -169,13 +192,13 @@ In this example, the deserializer buffers all `IAsyncEnumerable` contents in \* Non-`string` keys are not supported. -## Custom collections +### Custom collections Any collection type that isn't in one of the preceding namespaces is considered a custom collection. Such types include user-defined types and types defined by ASP.NET Core. For example, is in this group. All custom collections (everything that derives from `IEnumerable`) are supported for serialization, as long as their element types are supported. -### Custom collections with deserialization support +#### Deserialization support A custom collection is supported for deserialization if it: @@ -199,7 +222,7 @@ A custom collection is supported for deserialization if it: † See [Supported key types](#supported-key-types). -### Custom collections with known issues +#### Known issues There are known issues with the following custom collections: @@ -211,9 +234,9 @@ There are known issues with the following custom collections: For more information about known issues, see the [open issues in System.Text.Json](https://github.com/dotnet/runtime/issues?q=is%3Aopen+is%3Aissue+label%3Aarea-System.Text.Json). -## Supported key types +### Supported key types -Supported types for the keys of `Dictionary` and `SortedList` types include the following: +When used as the keys of `Dictionary` and `SortedList` types, the following types have built-in support: * `Boolean` * `Byte` @@ -230,11 +253,25 @@ Supported types for the keys of `Dictionary` and `SortedList` types include the * `SByte` * `Single` * `String` +* * `UInt16` * `UInt32` * `UInt64` +* +* + +In addition, the and methods let you add dictionary key support for any type of your choosing. + +## Unsupported types + +The following types aren't supported for serialization: + +* and +* , , and ref structs in general +* Delegate types +* and -## System.Data namespace +### System.Data namespace There are no built-in converters for , , and related types in the namespace. Deserializing these types from untrusted input is not safe, as explained in [the security guidance](../../../framework/data/adonet/dataset-datatable-dataview/security-guidance.md#safety-with-regard-to-untrusted-input). However, you can write a custom converter to support these types. For sample custom converter code that serializes and deserializes a `DataTable`, see [RoundtripDataTable.cs](https://github.com/dotnet/docs/blob/main/docs/standard/serialization/system-text-json/snippets/how-to/csharp/RoundtripDataTable.cs).