From 668cc3090e077d7de1fd2b57f36283f8605dbe2d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Oct 2025 09:40:49 -0400 Subject: [PATCH 1/5] Add errors and warnings related to the dynamic type and dynamic binding (#49432) * framework for next PR * Add all undocumented errors * reorganized by themes. * fix incorrect messages. * first edit pass. * final proofread. * fix incorrect recommendation on packages * Fix warnings and errors * one more set of warnings --- .github/prompts/error-consolidation.md | 10 +- .../dynamic-type-and-binding-errors.md | 257 ++++++++++++++++++ docs/csharp/language-reference/toc.yml | 6 + ...n-t-have-specifics-on-this-csharp-error.md | 28 +- 4 files changed, 270 insertions(+), 31 deletions(-) create mode 100644 docs/csharp/language-reference/compiler-messages/dynamic-type-and-binding-errors.md diff --git a/.github/prompts/error-consolidation.md b/.github/prompts/error-consolidation.md index 7d7b4ea99d553..4d96290538393 100644 --- a/.github/prompts/error-consolidation.md +++ b/.github/prompts/error-consolidation.md @@ -8,11 +8,11 @@ Overall steps: 1. Run Copilot search for other existing errors that person may have missed. 1. Search for missing errors. -## Add a single existing file into the new consolidated article. +## Add a single existing file into the new consolidated article. -We're going to work through a series of files consolidating errors and warnings related to declaring overloaded operators. +We're going to work through a series of files consolidating errors and warnings related to declaring the `dynamic` type and dynamic binding. -The destination for all these edits is the overloaded-operator-errors.md file. It already contains a skeleton for the final output. +The destination for all these edits is the dynamic-type-and-binding-errors.md file. It already contains a skeleton for the final output. For each source file I specify in this chat, you'll do the following tasks: @@ -28,7 +28,7 @@ For each source file I specify in this chat, you'll do the following tasks: ## Search for other related articles that may be missed. -Search all files in the docs/csharp/language-reference/compiler-messages and the docs/csharp/misc folder for any other errors and warnings that involve operator overloading. Give me a list to review for possible additional consolidation. Don't make any edits until the originating user approves. +Search all files in the docs/csharp/language-reference/compiler-messages and the docs/csharp/misc folder for any other errors and warnings that involve the `dynamic` type or dynamic binding. Give me a list to review for possible additional consolidation. Don't make any edits until the originating user approves. ## Final search in roslyn source @@ -51,7 +51,7 @@ Note that no redirections need to be added for these error codes. ## Build consolidated sections -For all remaining work, all edits will be in the `overloaded-operator-errors.md` file. The final format should mirror the structure of the `preprocessor-errors.md` file. Every H2 is a theme, all anchors are for the theme, not an individual error code. +For all remaining work, all edits will be in the `dynamic-type-and-binding-errors.md` file. The final format should mirror the structure of the `preprocessor-errors.md` file. Every H2 is a theme, all anchors are for the theme, not an individual error code. To do that, make a new H2 section for the theme. Remove all the H2s for the individual error codes that are part of that theme. Where applicable, the new H2 can include text or examples from the H2s you remove. The new section should include links to language reference articles that discuss the feature or theme. diff --git a/docs/csharp/language-reference/compiler-messages/dynamic-type-and-binding-errors.md b/docs/csharp/language-reference/compiler-messages/dynamic-type-and-binding-errors.md new file mode 100644 index 0000000000000..4a39a3436eef5 --- /dev/null +++ b/docs/csharp/language-reference/compiler-messages/dynamic-type-and-binding-errors.md @@ -0,0 +1,257 @@ +--- +title: Resolve errors related to dynamic binding and the dynamic type +description: These errors indicate an incorrect use of the `dynamic` type or an expression with runtime (or dynamic) binding. Learn about the errors and how to fix them. +f1_keywords: + - "CS1962" + - "CS1964" + - "CS1965" + - "CS1966" + - "CS1967" + - "CS1968" + - "CS1969" + - "CS1970" + - "CS1971" + - "CS1972" + - "CS1973" + - "CS1974" + - "CS1975" + - "CS1976" + - "CS1977" + - "CS1978" + - "CS1979" + - "CS1980" + - "CS1981" + - "CS7083" + - "CS8133" + - "CS8364" + - "CS8416" + - "CS9230" +helpviewer_keywords: + - "CS1962" + - "CS1964" + - "CS1965" + - "CS1966" + - "CS1967" + - "CS1968" + - "CS1969" + - "CS1970" + - "CS1971" + - "CS1972" + - "CS1973" + - "CS1974" + - "CS1975" + - "CS1976" + - "CS1977" + - "CS1978" + - "CS1979" + - "CS1980" + - "CS1981" + - "CS7083" + - "CS8133" + - "CS8364" + - "CS8416" + - "CS9230" +ms.date: 10/23/2025 +ai-usage: ai-assisted +--- +# Resolve warnings related to the dynamic type and dynamic binding + +This article covers the following compiler errors: + + +- [**CS1962**](#using-dynamic-in-type-declarations-and-constraints): *The `typeof` operator cannot be used on the `dynamic` type.* +- [**CS1964**](#dynamic-operation-restrictions): *Cannot apply dynamic conversion to an expression.* +- [**CS1965**](#using-dynamic-in-type-declarations-and-constraints): *Cannot derive from the `dynamic` type.* +- [**CS1966**](#using-dynamic-in-type-declarations-and-constraints): *Cannot derive from a constructed dynamic type.* +- [**CS1967**](#using-dynamic-in-type-declarations-and-constraints): *Cannot use the `dynamic` type as a type constraint.* +- [**CS1968**](#using-dynamic-in-type-declarations-and-constraints): *Cannot use a constructed dynamic type as a type constraint.* +- [**CS1969**](#missing-runtime-support-for-dynamic): *One or more types required to compile a dynamic expression cannot be found.* +- [**CS1970**](#using-dynamic-in-type-declarations-and-constraints): *Do not use '`System.Runtime.CompilerServices.DynamicAttribute`'. Use the '`dynamic`' keyword instead.* +- [**CS1971**](#dynamic-operation-restrictions): *The call to member needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.* +- [**CS1972**](#dynamic-operation-restrictions): *The indexer access needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.* +- [**CS1973**](#dynamic-operation-restrictions): *The dynamic argument type does not match the target parameter type for extension method.* +- [**CS1974**](#dynamic-dispatch-warnings): *Dynamic dispatch to a conditional method will fail at runtime.* +- [**CS1975**](#dynamic-operation-restrictions): *The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments.* +- [**CS1976**](#dynamic-operation-restrictions): *Cannot use a method group as an argument to a dynamically dispatched operation.* +- [**CS1977**](#dynamic-operation-restrictions): *Cannot use a lambda expression as an argument to a dynamically dispatched operation.* +- [**CS1978**](#dynamic-operation-restrictions): *Cannot use an expression as an argument to a dynamically dispatched operation.* +- [**CS1979**](#dynamic-operation-restrictions): *Query expressions with a source or join sequence of type dynamic are not allowed.* +- [**CS1980**](#missing-runtime-support-for-dynamic): *Cannot define a class or member that uses 'dynamic' because the compiler required type is missing.* +- [**CS1981**](#dynamic-dispatch-warnings): *The '`is dynamic`' pattern is misleading. Use '`is object`' instead.* +- [**CS7083**](#missing-runtime-support-for-dynamic): *Expression must be implicitly convertible to '`System.Object`', or the type '`dynamic`' is not available.* +- [**CS8133**](#dynamic-operation-restrictions): *Cannot deconstruct dynamic objects.* +- [**CS8364**](#dynamic-operation-restrictions): *An argument to '`nameof`' cannot use any dynamic operation.* +- [**CS8416**](#dynamic-operation-restrictions): *The async modifier cannot be used in the expression of a dynamic attribute.* +- [**CS9230**](#dynamic-operation-restrictions): *Cannot perform a dynamic invocation on an expression with type.* + +## Using `dynamic` in type declarations and constraints + +- **CS1962**: *The typeof operator cannot be used on the `dynamic` type.* +- **CS1965**: *Cannot derive from the `dynamic` type.* +- **CS1966**: *Cannot derive from a constructed dynamic type.* +- **CS1967**: *Cannot use the `dynamic` type as a type constraint.* +- **CS1968**: *Cannot use a constructed dynamic type as a type constraint.* +- **CS1970**: *Do not use '`System.Runtime.CompilerServices.DynamicAttribute`'. Use the '`dynamic`' keyword instead.* + +The [`dynamic` type](../builtin-types/reference-types.md#the-dynamic-type) provides late binding for operations at runtime. Use concrete types in contexts where the compiler needs type information at compile time, such as type declarations, constraints, inheritance, or reflection operations: + +- Use concrete types for reflection operations. Use specific types instead of `dynamic` with the `typeof` operator (CS1962) +- Use concrete types for inheritance. Specify a concrete base class instead of `dynamic` (CS1965, CS1966): +- Use concrete type constraints. Specify concrete type constraints on generic parameters instead of `dynamic` (CS1967, CS1968): +- Use the `dynamic` keyword for variables. Always use the `dynamic` keyword to declare dynamic variables. Don't apply the `DynamicAttribute` directly (CS1970): + +For more information about the `dynamic` type and its proper usage, see [Using type dynamic](../../advanced-topics/interop/using-type-dynamic.md). + +## Dynamic operation restrictions + +- **CS1964**: *Cannot apply dynamic conversion to an expression.* +- **CS1971**: *The call to member needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.* +- **CS1972**: *The indexer access needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.* +- **CS1973**: *The dynamic argument type does not match the target parameter type for extension method.* +- **CS1975**: *The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments.* +- **CS1976**: *Cannot use a method group as an argument to a dynamically dispatched operation.* +- **CS1977**: *Cannot use a lambda expression as an argument to a dynamically dispatched operation.* +- **CS1978**: *Cannot use an expression as an argument to a dynamically dispatched operation.* +- **CS1979**: *Query expressions with a source or join sequence of type dynamic are not allowed.* +- **CS8133**: *Cannot deconstruct dynamic objects.* +- **CS8364**: *An argument to '`nameof`' cannot use any dynamic operation.* +- **CS8416**: *The async modifier cannot be used in the expression of a dynamic attribute.* +- **CS9230**: *Cannot perform a dynamic invocation on an expression with type.* + +While [dynamic binding](../operators/member-access-operators.md#member-access-expression-) provides flexibility at runtime, cast dynamic values to specific types when you need compile-time type information for certain operations. + +- **Cast dynamic arguments before calling base members:** Cast dynamic arguments to their specific types before calling base members, indexers, or constructors (CS1971, CS1972, CS1975): + + ```csharp + class Base + { + public virtual void Method(object obj) { } + public virtual int this[int index] => 0; + public Base(int value) { } + } + + class Derived : Base + { + public Derived(dynamic value) : base((int)value) { } // Cast before calling base constructor + + public override void Method(object obj) + { + dynamic d = obj; + base.Method((object)d); // Cast before calling base method + } + + public override int this[int index] + { + get + { + dynamic d = index; + return base[(int)d]; // Cast before accessing base indexer + } + } + } + ``` + +- **Cast to specific types before passing delegates or lambdas:** Cast the dynamic object to its concrete type before passing method groups, lambda expressions, or delegates (CS1976, CS1977, CS1978): + + ```csharp + dynamic d = GetDynamicObject(); + + // Avoid: + d.ProcessData(Console.WriteLine); // CS1976 + d.ProcessData(x => x * 2); // CS1977 + + // Recommended: + ((IProcessor)d).ProcessData(Console.WriteLine); // Cast first + ((IProcessor)d).ProcessData(x => x * 2); // Cast first + ``` + +- **Use concrete types for LINQ queries:** Use a concrete type instead of `dynamic` for LINQ query sources and join sequences (CS1979): + + ```csharp + dynamic data = GetData(); + + // Avoid: + var query = from item in data // CS1979 + select item; + + // Recommended: + IEnumerable typedData = data; + var query = from item in typedData + select item; + ``` + +- **Access tuple elements individually:** Access tuple elements individually instead of using deconstruction with dynamic tuples (CS8133): + + ```csharp + dynamic tuple = (1, 2); + + // Avoid: + var (a, b) = tuple; // CS8133 + + // Recommended: + var a = tuple.Item1; + var b = tuple.Item2; + ``` + +- **Use compile-time expressions for operations requiring type information:** Use concrete types for operations that need compile-time type information (CS1964, CS1973, CS8364, CS8416, CS9230): + + ```csharp + dynamic value = GetValue(); + + // Avoid: + var name = nameof(value.Property); // CS8364 + + // Recommended: + MyType typedValue = value; + var name = nameof(typedValue.Property); // Use concrete type + ``` + +For more information about dynamic binding and its limitations, see [Using type dynamic](../../advanced-topics/interop/using-type-dynamic.md). + +## Missing runtime support for dynamic + +- **CS1969**: *One or more types required to compile a dynamic expression cannot be found. Are you missing a reference to 'Microsoft.CSharp.dll'?* +- **CS1980**: *Cannot define a class or member that uses 'dynamic' because the compiler required type cannot be found. Are you missing a reference?* +- **CS7083**: *Expression must be implicitly convertible to '`System.Object`', or the type '`dynamic`' is not available.* + +The compiler needs types from the `System.Runtime` namespace and the Dynamic Language Runtime (DLR) to generate code for dynamic operations (CS1969, CS1980, CS7083). Ensure your project includes the necessary references. The required types are included in all modern .NET (.NET 5 and later) projects. For .NET Framework projects, add a reference to `Microsoft.CSharp.dll` in your project file. + +For more information about dynamic type requirements, see [Using type dynamic](../../advanced-topics/interop/using-type-dynamic.md). + +## Dynamic dispatch warnings + +- **CS1974**: *The dynamically dispatched call to method can fail at run-time because one or more applicable overloads are conditional methods.* +- **CS1981**: *The '`is dynamic`' pattern is misleading. The runtime type of the subexpression is never '`dynamic`'. Consider using '`object`' instead.* + +When you call methods that have the `[Conditional]` attribute, avoid using dynamic dispatch. The compiler can't verify conditional method attributes with dynamic binding, which can cause runtime failures (CS1974). Cast the dynamic expression to its actual type first: + +```csharp +dynamic d = GetObject(); + +// Avoid: +d.ConditionalMethod(); // CS1974 - can fail at runtime + +// Recommended: +MyClass obj = (MyClass)d; +obj.ConditionalMethod(); // Compile-time checks ensure correctness +``` + +When you check whether a value is non-null, use `is object` instead of `is dynamic`. The `dynamic` keyword is a compile-time construct, and no object's runtime type is ever `dynamic` (CS1981): + +```csharp +// Avoid: +if (someValue is dynamic) // CS1981 - always evaluates to false +{ + // This code never executes +} + +// Recommended: +if (someValue is object) // Correctly checks if non-null +{ + // This code executes for non-null values +} +``` + +For more information about dynamic dispatch and runtime behavior, see [Using type dynamic](../../advanced-topics/interop/using-type-dynamic.md). diff --git a/docs/csharp/language-reference/toc.yml b/docs/csharp/language-reference/toc.yml index 801dd5dafa144..8a866c5b23fe1 100644 --- a/docs/csharp/language-reference/toc.yml +++ b/docs/csharp/language-reference/toc.yml @@ -619,6 +619,12 @@ items: - name: Thread synchronization href: ./compiler-messages/lock-semantics.md displayName: CS0185, CS9216, CS9217 + - name: Dynamic expressions + href: ./compiler-messages/dynamic-type-and-binding-errors.md + displayName: > + CS1962, CS1964, CS1965, CS1966, CS1967, CS1968, CS1969, CS1970, CS1971, CS1972, + CS1973, CS1974, CS1975, CS1976, CS1977, CS1978, CS1979, CS1980, CS1981, CS7083, + CS8133, CS8364, CS8416, CS9230 - name: Unsafe code href: ./compiler-messages/unsafe-code-errors.md displayName: > diff --git a/docs/csharp/misc/sorry-we-don-t-have-specifics-on-this-csharp-error.md b/docs/csharp/misc/sorry-we-don-t-have-specifics-on-this-csharp-error.md index 8e73511c27bc4..0e67559e90914 100644 --- a/docs/csharp/misc/sorry-we-don-t-have-specifics-on-this-csharp-error.md +++ b/docs/csharp/misc/sorry-we-don-t-have-specifics-on-this-csharp-error.md @@ -38,26 +38,7 @@ f1_keywords: - "CS1774" - "CS1960" - "CS1961" - - "CS1962" - - "CS1964" - - "CS1965" - - "CS1966" - - "CS1967" - - "CS1968" - - "CS1969" - - "CS1970" - - "CS1971" - - "CS1972" - - "CS1973" - - "CS1974" - - "CS1975" - - "CS1976" - - "CS1977" - - "CS1978" - - "CS1979" - - "CS1980" - - "CS1981" - - "CS1984" + - "CS1982" - "CS1985" - "CS1989" - "CS1991" @@ -154,8 +135,7 @@ f1_keywords: - "CS7080" - "CS7081" - "CS7082" - - "CS7083" - - "CS7085" + - "CS7084" - "CS7086" - "CS7087" - "CS7088" @@ -249,7 +229,6 @@ f1_keywords: - "CS8123" - "CS8126" - "CS8128" - - "CS8133" - "CS8134" - "CS8135" - "CS8136" @@ -306,7 +285,6 @@ f1_keywords: - "CS8360" - "CS8361" - "CS8362" - - "CS8364" - "CS8372" - "CS8375" - "CS8377" @@ -324,7 +302,6 @@ f1_keywords: - "CS8413" - "CS8414" - "CS8415" - - "CS8416" - "CS8417" - "CS8418" - "CS8419" @@ -577,7 +554,6 @@ f1_keywords: - "CS9097" # C# 12 errors begin here - "CS9229" # Modifiers cannot be placed on using declarations (using declarations) - - "CS9230" # Cannot perform a dynamic invocation on an expression with type 'type'. (dynamic binding) # C# 14 errors begin here - "CS9327" - "CS9328" From e0daf3a5d4e137b1de4857f37f8074888a5377a4 Mon Sep 17 00:00:00 2001 From: Stuart Mosquera Date: Fri, 24 Oct 2025 10:44:20 -0300 Subject: [PATCH 2/5] add CS8500 compiler warning (#49433) --- docs/csharp/misc/cs8500.md | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docs/csharp/misc/cs8500.md diff --git a/docs/csharp/misc/cs8500.md b/docs/csharp/misc/cs8500.md new file mode 100644 index 0000000000000..f37a457529f62 --- /dev/null +++ b/docs/csharp/misc/cs8500.md @@ -0,0 +1,60 @@ +--- +description: "Learn more about: Compiler Warning (level 4) CS8500" +title: "Compiler Warning (level 4) CS8500" +ms.date: 10/23/2025 +f1_keywords: + - "CS8500" +helpviewer_keywords: + - "CS8500" +--- + +# Compiler Warning (level 4) CS8500 + +This takes the address of, gets the size of, or declares a pointer to a managed type ('type') + +Even when used with the [unsafe](../language-reference/keywords/unsafe.md) keyword, taking the address of a managed object, getting the size of a managed object, or declaring a pointer to a managed type is not allowed. A managed type is: + +- Any reference type +- Any struct that contains a reference type as a field or property + +For more information, see [Unmanaged types](../language-reference/builtin-types/unmanaged-types.md). + +The following sample generates CS8500: + +```csharp +// CS8500.cs +// Compile with: /unsafe + +class MyClass +{ + int a = 98; +} + +struct MyProblemStruct +{ + string s; + float f; +} + +struct MyGoodStruct +{ + int i; + float f; +} + +public class Program +{ + unsafe public static void Main() + { + // MyClass is a class, a managed type. + MyClass s = new MyClass(); + MyClass* s2 = &s; // CS8500 + + // The struct contains a string, a managed type. + int i = sizeof(MyProblemStruct); // CS8500 + + // The struct contains only value types. + i = sizeof(MyGoodStruct); // OK + } +} +``` From 2a6a936b04fbb57b212394fef6b893877fbfde5f Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 09:58:59 -0700 Subject: [PATCH 3/5] [UUF] Fix incorrect variable names in Console.WriteLine for Boolean code examples (#49439) --- .../System/Boolean/Overview/csharp/binary1.cs | 2 +- .../System/Boolean/Overview/csharp/size1.cs | 16 ++++++++-------- .../System/Boolean/Overview/vb/binary1.vb | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/binary1.cs b/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/binary1.cs index 56975c83825a4..157211ddb538c 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/binary1.cs +++ b/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/binary1.cs @@ -14,7 +14,7 @@ public static void Main() Console.WriteLine($"Binary value: {value} ({GetBinaryString(value)})"); // Restore the flag from its binary representation. bool newFlag = BitConverter.ToBoolean(new Byte[] { value }, 0); - Console.WriteLine($"Restored value: {flag}{Environment.NewLine}"); + Console.WriteLine($"Restored value: {newFlag}{Environment.NewLine}"); } } diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/size1.cs b/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/size1.cs index bafa129c0ee8e..2e145259d26dc 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/size1.cs +++ b/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/csharp/size1.cs @@ -20,10 +20,10 @@ public static void Main() Console.WriteLine($"Size of BoolStruct: {sizeof(BoolStruct)}"); Console.WriteLine("Field offsets:"); Console.WriteLine($" flag1: {(bool*) &b.flag1 - addr}"); - Console.WriteLine($" flag1: {(bool*) &b.flag2 - addr}"); - Console.WriteLine($" flag1: {(bool*) &b.flag3 - addr}"); - Console.WriteLine($" flag1: {(bool*) &b.flag4 - addr}"); - Console.WriteLine($" flag1: {(bool*) &b.flag5 - addr}"); + Console.WriteLine($" flag2: {(bool*) &b.flag2 - addr}"); + Console.WriteLine($" flag3: {(bool*) &b.flag3 - addr}"); + Console.WriteLine($" flag4: {(bool*) &b.flag4 - addr}"); + Console.WriteLine($" flag5: {(bool*) &b.flag5 - addr}"); } } } @@ -31,8 +31,8 @@ public static void Main() // Size of BoolStruct: 5 // Field offsets: // flag1: 0 -// flag1: 1 -// flag1: 2 -// flag1: 3 -// flag1: 4 +// flag2: 1 +// flag3: 2 +// flag4: 3 +// flag5: 4 // diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/vb/binary1.vb b/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/vb/binary1.vb index 35f18017a8944..1f05bc46a8579 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/vb/binary1.vb +++ b/docs/fundamentals/runtime-libraries/snippets/System/Boolean/Overview/vb/binary1.vb @@ -13,7 +13,7 @@ Module Example1 GetBinaryString(value)) ' Restore the flag from its binary representation. Dim newFlag As Boolean = BitConverter.ToBoolean({value}, 0) - Console.WriteLine("Restored value: {0}", flag) + Console.WriteLine("Restored value: {0}", newFlag) Console.WriteLine() Next End Sub From 1b72982db85ed1b46cb172b7242e83937b04affc Mon Sep 17 00:00:00 2001 From: "Meaghan Osagie (Lewis)" Date: Fri, 24 Oct 2025 14:56:52 -0700 Subject: [PATCH 4/5] Freshness pass for dotnet tutorials (#49431) * Freshness pass for dotnet tutorials * Modernize code samples * Update cli-templates-create-item-template.md * update versioning details --- .../cli-templates-create-item-template.md | 34 +++++----- .../cli-templates-create-project-template.md | 18 ++--- .../cli-templates-create-template-package.md | 23 ++++--- .../creating-app-with-plugin-support.md | 24 +++---- .../debugging-with-visual-studio-code.md | 4 +- .../tutorials/debugging-with-visual-studio.md | 7 +- docs/core/tutorials/index.md | 3 +- docs/core/tutorials/libraries.md | 7 +- .../tutorials/library-with-visual-studio.md | 5 +- docs/core/tutorials/netcore-hosting.md | 25 +++---- .../publishing-with-visual-studio.md | 5 +- .../csharp/StringLibrary/Class1.cs | 16 ++--- .../csharp/StringLibraryTest/UnitTest1.cs | 67 +++++++++---------- .../csharp/StringLibraryTestNet8/UnitTest1.cs | 67 +++++++++---------- ...testing-library-with-visual-studio-code.md | 5 +- .../testing-library-with-visual-studio.md | 35 +++++----- docs/core/tutorials/testing-with-cli.md | 5 +- 17 files changed, 165 insertions(+), 185 deletions(-) diff --git a/docs/core/tutorials/cli-templates-create-item-template.md b/docs/core/tutorials/cli-templates-create-item-template.md index c22f54acbd873..410d8199997fb 100644 --- a/docs/core/tutorials/cli-templates-create-item-template.md +++ b/docs/core/tutorials/cli-templates-create-item-template.md @@ -3,7 +3,8 @@ title: Create an item template for dotnet new - .NET CLI titleSuffix: "" description: Learn how to create an item template for the dotnet new command. Item templates can contain any number of files. author: adegeo -ms.date: 09/08/2023 +ms.date: 10/23/2025 +ai-usage: ai-assisted ms.topic: tutorial ms.author: adegeo --- @@ -17,7 +18,7 @@ You can view the completed template in the [.NET Samples GitHub repository](http > [!TIP] > **Item** templates aren't shown in the **Add** > **New Item** dialog of Visual Studio. -In this part of the series, you'll learn how to: +In this part of the series, you learn how to: > [!div class="checklist"] > @@ -29,13 +30,11 @@ In this part of the series, you'll learn how to: ## Prerequisites -* [.NET SDK 7.0.100](https://dotnet.microsoft.com/download) or a later version. +- [.NET 9](https://dotnet.microsoft.com/download) or a later version. - The reference article explains the basics about templates and how they're put together. Some of this information is reiterated here. +- The reference article explains the basics about templates and how they're put together. Some of this information is reiterated here. -* Open a terminal and navigate to a folder where you'll store and test the templates. - -[!INCLUDE [dotnet6-syntax-note](includes/dotnet6-syntax-note.md)] +- Open a terminal and navigate to a folder where you'll store and test the templates. ## Create the required folders @@ -54,7 +53,7 @@ parent_folder ## Create an item template -An item template is a specific type of template that contains one or more files. These types of templates are useful when you already have a project and you want to generate another file, like a config file or code file. In this example, you'll create a class that adds an extension method to the string type. +An item template is a specific type of template that contains one or more files. These types of templates are useful when you already have a project and you want to generate another file, like a config file or code file. In this example, you create a class that adds an extension method to the string type. In your terminal, navigate to the _working\content_ folder and create a new subfolder named _extensions_. @@ -64,7 +63,7 @@ working └───extensions ``` -Navigate to the _extensions_ folder and create a new file named _StringExtensions.cs_. Open the file in a text editor. This class will provide an extension method named `Reverse` that reverses the contents of a string. Paste in the following code and save the file: +Navigate to the _extensions_ folder and create a new file named _StringExtensions.cs_. Open the file in a text editor. This class provides an extension method named `Reverse` that reverses the contents of a string. Paste in the following code and save the file: ```csharp namespace System; @@ -80,7 +79,7 @@ public static class StringExtensions } ``` -Now that the content of the template is finished, the next step is to create the template config. +Now that the content of the template is finished, create the template config. ## Create the template config @@ -171,7 +170,7 @@ Template options: Default: StringExtensions ``` -Now that you have a valid _.template.config/template.json_ file, your template is ready to be installed. In your terminal, navigate to the _extensions_ folder and run the following command to install the template located at the current folder: +Now that you have a valid _.template.config/template.json_ file, your template is ready to be installed. In your terminal, navigate to the _extensions_ folder and run the following command to install the template located at the current folder: * **On Windows**: `dotnet new install .\` * **On Linux or macOS**: `dotnet new install ./` @@ -192,8 +191,9 @@ Example templates: string extensions stringext [C#] Now that you have an item template installed, test it. -01. Navigate to the _test_ folder. -01. Create a new console application with `dotnet new console`, which generates a working project you can easily test with the `dotnet run` command. +1. Navigate to the _test_ folder. + +1. Create a new console application with `dotnet new console`, which generates a working project you can easily test with the `dotnet run` command. ```dotnetcli dotnet new console @@ -211,7 +211,7 @@ Now that you have an item template installed, test it. Restore succeeded. ``` -01. Run the project using the following command. +1. Run the project using the following command. ```dotnetcli dotnet run @@ -223,7 +223,7 @@ Now that you have an item template installed, test it. Hello, World! ``` -01. Run `dotnet new stringext` to generate the _StringExtensions.cs_ file from the template. +1. Run `dotnet new stringext` to generate the _StringExtensions.cs_ file from the template. ```dotnetcli dotnet new stringext @@ -235,7 +235,7 @@ Now that you have an item template installed, test it. The template "Example templates: string extensions" was created successfully. ``` -01. Change the code in _Program.cs_ to reverse the `"Hello, World!"` string with the extension method provided by the template. +1. Change the code in _Program.cs_ to reverse the `"Hello, World!"` string with the extension method provided by the template. ```csharp Console.WriteLine("Hello, World!".Reverse()); @@ -257,7 +257,7 @@ Congratulations! You created and deployed an item template with .NET. In prepara ## Uninstall the template -In your terminal, navigate to the _extensions_ folder and run the following command to uninstall the templates located at the current folder: +In your terminal, navigate to the _extensions_ folder and run the following command to uninstall the templates located at the current folder: * **On Windows**: `dotnet new uninstall .\` * **On Linux or macOS**: `dotnet new uninstall ./` diff --git a/docs/core/tutorials/cli-templates-create-project-template.md b/docs/core/tutorials/cli-templates-create-project-template.md index 0809c59f00c51..b634004e4d549 100644 --- a/docs/core/tutorials/cli-templates-create-project-template.md +++ b/docs/core/tutorials/cli-templates-create-project-template.md @@ -2,14 +2,15 @@ title: Create a project template for dotnet new description: Learn how to create a project template for the dotnet new command. author: adegeo -ms.date: 09/08/2023 +ms.date: 10/23/2025 +ai-usage: ai-assisted ms.topic: tutorial ms.author: adegeo --- # Tutorial: Create a project template -With .NET, you can create and deploy templates that generate projects, files, even resources. This tutorial is part two of a series that teaches you how to create, install, and uninstall, templates for use with the `dotnet new` command. +With .NET, you can create and deploy templates that generate projects, files, and resources. This tutorial is part two of a series that teaches you how to create, install, and uninstall templates for use with the `dotnet new` command. > [!TIP] > The official .NET templates that are shipped with the .NET SDK can be found in the following repositories: @@ -26,7 +27,7 @@ With .NET, you can create and deploy templates that generate projects, files, ev > > You can view the templates that are installed on your machine by running the `dotnet new list` command. -In this part of the series you'll learn how to: +In this part of the series, you learn how to: > [!div class="checklist"] > @@ -38,16 +39,17 @@ In this part of the series you'll learn how to: ## Prerequisites -* Complete [part 1](cli-templates-create-item-template.md) of this tutorial series. -* Open a terminal and navigate to the _working\content_ folder. +- [.NET 9](https://dotnet.microsoft.com/download) or a later version. -[!INCLUDE [dotnet6-syntax-note](includes/dotnet6-syntax-note.md)] +- Complete [part 1](cli-templates-create-item-template.md) of this tutorial series. + +- Open a terminal and navigate to the _working\content_ folder. ## Create a project template Project templates produce ready-to-run projects that make it easy for users to start with a working set of code. .NET includes a few project templates such as a console application or a class library. In this example, you create a new console application project that replaces the standard "Hello World" console output with one that runs asynchronously. -In your terminal, navigate to the _working\content_ folder and create a new subfolder named _consoleasync_. Enter the subfolder and run `dotnet new console` to generate the standard console application. You'll edit the files produced by this template to create a new template. +In your terminal, navigate to the _working\content_ folder and create a new subfolder named _consoleasync_. Enter the subfolder and run `dotnet new console` to generate the standard console application. Edit the files produced by this template to create a new template. ```console working @@ -59,7 +61,7 @@ working ## Modify Program.cs -Open up the _Program.cs_ file. The standard console project doesn't asynchronously write to the console output, so let's add that. Change the code to the following and save the file: +Open up the _Program.cs_ file. The standard console project doesn't asynchronously write to the console output, so add that. Change the code to the following and save the file: ```csharp // See https://aka.ms/new-console-template for more information diff --git a/docs/core/tutorials/cli-templates-create-template-package.md b/docs/core/tutorials/cli-templates-create-template-package.md index cc695663f8aa1..9337a17559539 100644 --- a/docs/core/tutorials/cli-templates-create-template-package.md +++ b/docs/core/tutorials/cli-templates-create-template-package.md @@ -2,18 +2,19 @@ title: Create a template package for dotnet new description: Learn how to create a csproj file that builds a template package for the dotnet new command. author: adegeo -ms.date: 09/11/2023 +ms.date: 10/23/2025 +ai-usage: ai-assisted ms.topic: tutorial ms.author: adegeo --- # Tutorial: Create a template package -With .NET, you can create and deploy templates that generate projects, files, and even resources. This tutorial is part three of a series that teaches you how to create, install, and uninstall templates for use with the `dotnet new` command. +With .NET, you can create and deploy templates that generate projects, files, and resources. This tutorial is part three of a series that teaches you how to create, install, and uninstall templates for use with the `dotnet new` command. You can view the completed template in the [.NET Samples GitHub repository](https://github.com/dotnet/samples/tree/main/core/tutorials/cli-templates-create-item-template). -In this part of the series you'll learn how to: +In this part of the series, you learn how to: > [!div class="checklist"] > @@ -23,16 +24,16 @@ In this part of the series you'll learn how to: ## Prerequisites -* Complete [part 1](cli-templates-create-item-template.md) and [part 2](cli-templates-create-project-template.md) of this tutorial series. +- [.NET 9](https://dotnet.microsoft.com/download) or a later version. - This tutorial uses the two templates created in the first two parts of this tutorial series. You can use a different template as long as you copy the template, as a folder, into the _working\content_ folder. +- Complete [part 1](cli-templates-create-item-template.md) and [part 2](cli-templates-create-project-template.md) of this tutorial series. -* Open a terminal and navigate to the _working_ folder. + This tutorial uses the two templates created in the first two parts of this tutorial series. You can use a different template as long as you copy the template, as a folder, into the _working\content_ folder. -* Install .NET 8 or .NET 9. -* Install the `Microsoft.TemplateEngine.Authoring.Templates` template from the NuGet package feed. +- Open a terminal and navigate to the _working_ folder. - * Run the `dotnet new install Microsoft.TemplateEngine.Authoring.Templates` command from your terminal. +- Install the `Microsoft.TemplateEngine.Authoring.Templates` template from the NuGet package feed. + - Run the `dotnet new install Microsoft.TemplateEngine.Authoring.Templates` command from your terminal. ## Create a template package project @@ -42,11 +43,11 @@ Template packages are represented by a NuGet package (_.nupkg_) file. And, like Normally you use a C# project file to compile code and produce a binary. However, the project can also be used to generate a template package. By changing the settings of the _.csproj_, you can prevent it from compiling any code and instead include all the assets of your templates as resources. When this project is built, it produces a template package NuGet package. -The package you're going to generate will include the [item](cli-templates-create-item-template.md) and [project](cli-templates-create-project-template.md) templates previously created. +The package you're going to generate includes the [item](cli-templates-create-item-template.md) and [project](cli-templates-create-project-template.md) templates previously created. The [Microsoft.TemplateEngine.Authoring.Templates](https://www.nuget.org/packages/Microsoft.TemplateEngine.Authoring.Templates) package contains templates useful for template authoring. To install this package, nuget.org should be available as NuGet feed in the working directory. -01. In the _working_ folder, run the following command to create the template package: +1. In the _working_ folder, run the following command to create the template package: ```dotnetcli dotnet new templatepack -n "AdatumCorporation.Utility.Templates" diff --git a/docs/core/tutorials/creating-app-with-plugin-support.md b/docs/core/tutorials/creating-app-with-plugin-support.md index 0629ecd2a10ec..5c5dab5a27b49 100644 --- a/docs/core/tutorials/creating-app-with-plugin-support.md +++ b/docs/core/tutorials/creating-app-with-plugin-support.md @@ -157,16 +157,12 @@ static IEnumerable CreateCommands(Assembly assembly) { int count = 0; - foreach (Type type in assembly.GetTypes()) + foreach (var type in assembly.GetTypes().Where(t => typeof(ICommand).IsAssignableFrom(t))) { - if (typeof(ICommand).IsAssignableFrom(type)) + if (Activator.CreateInstance(type) is ICommand result) { - ICommand result = Activator.CreateInstance(type) as ICommand; - if (result != null) - { - count++; - yield return result; - } + count++; + yield return result; } } @@ -194,17 +190,13 @@ Now that the `AppWithPlugin` project has the `PluginLoadContext` type, update th static Assembly LoadPlugin(string relativePath) { // Navigate up to the solution root - string root = Path.GetFullPath(Path.Combine( - Path.GetDirectoryName( - Path.GetDirectoryName( - Path.GetDirectoryName( - Path.GetDirectoryName( - Path.GetDirectoryName(typeof(Program).Assembly.Location))))))); + string root = Path.GetFullPath( + Path.Combine(typeof(Program).Assembly.Location, "..", "..", "..", "..", "..")); string pluginLocation = Path.GetFullPath(Path.Combine(root, relativePath.Replace('\\', Path.DirectorySeparatorChar))); Console.WriteLine($"Loading commands from: {pluginLocation}"); - PluginLoadContext loadContext = new PluginLoadContext(pluginLocation); - return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(pluginLocation))); + PluginLoadContext loadContext = new(pluginLocation); + return loadContext.LoadFromAssemblyName(new(Path.GetFileNameWithoutExtension(pluginLocation))); } ``` diff --git a/docs/core/tutorials/debugging-with-visual-studio-code.md b/docs/core/tutorials/debugging-with-visual-studio-code.md index 46f1273a979e9..5c5d712a4faa7 100644 --- a/docs/core/tutorials/debugging-with-visual-studio-code.md +++ b/docs/core/tutorials/debugging-with-visual-studio-code.md @@ -1,7 +1,7 @@ --- title: Debug a .NET console application using Visual Studio Code description: Learn how to debug a .NET console app using Visual Studio Code. -ms.date: 09/12/2024 +ms.date: 10/23/2025 --- # Tutorial: Debug a .NET console application using Visual Studio Code @@ -9,7 +9,7 @@ This tutorial introduces the debugging tools available in Visual Studio Code for ## Prerequisites -- This tutorial works with the console app that you create in [Create a .NET console application using Visual Studio Code](with-visual-studio-code.md). +This tutorial works with the console app that you create in [Create a .NET console application using Visual Studio Code](with-visual-studio-code.md). ## Use Debug build configuration diff --git a/docs/core/tutorials/debugging-with-visual-studio.md b/docs/core/tutorials/debugging-with-visual-studio.md index 3286db99dbb77..d7e0042308df9 100644 --- a/docs/core/tutorials/debugging-with-visual-studio.md +++ b/docs/core/tutorials/debugging-with-visual-studio.md @@ -1,7 +1,8 @@ --- title: Debug a .NET console application using Visual Studio description: Learn how to debug a .NET console app using Visual Studio. -ms.date: 08/21/2023 +ms.date: 10/23/2025 +ai-usage: ai-assisted dev_langs: - "csharp" - "vb" @@ -16,7 +17,7 @@ This tutorial introduces the debugging tools available in Visual Studio. ## Prerequisites -- This tutorial works with the console app that you create in [Create a .NET console application using Visual Studio](with-visual-studio.md). +This tutorial works with the console app that you create in [Create a .NET console application using Visual Studio](with-visual-studio.md). ## Use Debug build configuration @@ -24,7 +25,7 @@ This tutorial introduces the debugging tools available in Visual Studio. In the Debug configuration, a program compiles with full symbolic debug information and no optimization. Optimization complicates debugging, because the relationship between source code and generated instructions is more complex. The release configuration of a program has no symbolic debug information and is fully optimized. - By default, Visual Studio uses the Debug build configuration, so you don't need to change it before debugging. +By default, Visual Studio uses the Debug build configuration, so you don't need to change it before debugging. 1. Start Visual Studio. diff --git a/docs/core/tutorials/index.md b/docs/core/tutorials/index.md index b197764e75836..0314bbfd9a2aa 100644 --- a/docs/core/tutorials/index.md +++ b/docs/core/tutorials/index.md @@ -1,7 +1,8 @@ --- title: .NET Tutorials description: Follow tutorials for learning .NET to build apps and libraries on Mac, Linux, and Windows. -ms.date: 06/22/2022 +ms.date: 10/23/2025 +ai-usage: ai-assisted ms.custom: devdivchpfy22 titleSuffix: "" --- diff --git a/docs/core/tutorials/libraries.md b/docs/core/tutorials/libraries.md index c043661b226d9..61476f046f9ee 100644 --- a/docs/core/tutorials/libraries.md +++ b/docs/core/tutorials/libraries.md @@ -2,11 +2,12 @@ title: Develop libraries with the .NET CLI description: Learn how to create .NET libraries using the .NET CLI. You'll create a library that supports multiple frameworks. ms.topic: how-to -ms.date: 11/23/2021 +ms.date: 10/23/2025 +ai-usage: ai-assisted --- # Develop libraries with the .NET CLI -This article covers how to write libraries for .NET using the .NET CLI. The CLI provides an efficient and low-level experience that works across any supported OS. You can still build libraries with Visual Studio, and if that is your preferred experience [refer to the Visual Studio guide](library-with-visual-studio.md). +This article covers how to write libraries for .NET using the .NET CLI. The CLI provides an efficient and low-level experience that works across any supported OS. You can still build libraries with Visual Studio, and if that's your preferred experience [refer to the Visual Studio guide](library-with-visual-studio.md). ## Prerequisites @@ -28,7 +29,7 @@ Additionally, if you wish to support older .NET Framework targets, you need to i ## How to target .NET 5+ or .NET Standard -You control your project's target framework by adding it to your project file (*.csproj* or *.fsproj*). For guidance on how to choose between targeting .NET 5+ or .NET Standard see [.NET 5+ and .NET Standard](../../standard/net-standard.md#net-5-and-net-standard). +You control your project's target framework by adding it to your project file (*.csproj* or *.fsproj*). For guidance on how to choose between targeting .NET 5+ or .NET Standard, see [.NET 5+ and .NET Standard](../../standard/net-standard.md#net-5-and-net-standard). ```xml diff --git a/docs/core/tutorials/library-with-visual-studio.md b/docs/core/tutorials/library-with-visual-studio.md index a97c6c6554a53..537e7b5c0bfbc 100644 --- a/docs/core/tutorials/library-with-visual-studio.md +++ b/docs/core/tutorials/library-with-visual-studio.md @@ -1,7 +1,8 @@ --- title: Create a .NET class library using Visual Studio description: Learn how to create a .NET class library using Visual Studio. -ms.date: 03/21/2024 +ms.date: 10/23/2025 +ai-usage: ai-assisted dev_langs: - "csharp" - "vb" @@ -23,7 +24,7 @@ When you create a class library, you can distribute it as a NuGet package or as ## Create a solution -Start by creating a blank solution to put the class library project in. A Visual Studio solution serves as a container for one or more projects. You'll add additional, related projects to the same solution. +Start by creating a blank solution to put the class library project in. A Visual Studio solution serves as a container for one or more projects. You'll add related projects to the same solution. To create the blank solution: diff --git a/docs/core/tutorials/netcore-hosting.md b/docs/core/tutorials/netcore-hosting.md index a7096a0c8a7a4..9c7272d6d6d0c 100644 --- a/docs/core/tutorials/netcore-hosting.md +++ b/docs/core/tutorials/netcore-hosting.md @@ -1,29 +1,28 @@ --- title: Write a custom .NET runtime host -description: Learn to host the .NET runtime from native code to support advanced scenarios that require controlling how the .NET runtime works. +description: Learn to host the .NET runtime from native code to support advanced scenarios that require controlling how the .NET runtime works. author: mjrousos ms.topic: how-to -ms.date: 12/21/2018 +ms.date: 10/23/2025 +ai-usage: ai-assisted --- # Write a custom .NET host to control the .NET runtime from your native code Like all managed code, .NET applications are executed by a host. The host is responsible for starting the runtime (including components like the JIT and garbage collector) and invoking managed entry points. -Hosting the .NET runtime is an advanced scenario and, in most cases, .NET developers don't need to worry about hosting because .NET build processes provide a default host to run .NET applications. In some specialized circumstances, though, it can be useful to explicitly host the .NET runtime, either as a means of invoking managed code in a native process or in order to gain more control over how the runtime works. +Hosting the .NET runtime is an advanced scenario and, in most cases, .NET developers don't need to worry about hosting because .NET build processes provide a default host to run .NET applications. In some specialized circumstances, though, it can be useful to explicitly host the .NET runtime, either as a means of invoking managed code in a native process or to gain more control over how the runtime works. This article gives an overview of the steps necessary to start the .NET runtime from native code and execute managed code in it. ## Prerequisites -Because hosts are native applications, this tutorial covers constructing a C++ application to host .NET. You will need a C++ development environment (such as that provided by [Visual Studio](https://aka.ms/vsdownload?utm_source=mscom&utm_campaign=msdocs)). +Because hosts are native applications, this tutorial covers constructing a C++ application to host .NET. You need a C++ development environment (such as that provided by [Visual Studio](https://aka.ms/vsdownload?utm_source=mscom&utm_campaign=msdocs)). -You will also need to build a .NET component to test the host with, so you should install the [.NET SDK](https://dotnet.microsoft.com/download). It includes the necessary headers and libraries to link with. As an example, on Windows with the .NET 8 SDK the files can be found in `C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native`. +You also need to build a .NET component to test the host with, so you should install the [latests .NET SDK](https://dotnet.microsoft.com/download). It includes the necessary headers and libraries to link with. ## Hosting APIs -Hosting the .NET runtime in .NET Core 3.0 and above is done with the `nethost` and `hostfxr` libraries' APIs. These entry points handle the complexity of finding and setting up the runtime for initialization and allow both launching a managed application and calling into a static managed method. - -Prior to .NET Core 3.0, the only option for hosting the runtime was through the [`coreclrhost.h`](https://github.com/dotnet/runtime/blob/main/src/coreclr/hosts/inc/coreclrhost.h) API. This hosting API is obsolete now and should not be used for hosting .NET Core 3.0 and higher runtimes. +Hosting the .NET runtime is done with the `nethost` and `hostfxr` libraries' APIs. These entry points handle the complexity of finding and setting up the runtime for initialization and allow both launching a managed application and calling into a static managed method. ## Create a host using `nethost.h` and `hostfxr.h` @@ -55,13 +54,9 @@ The sample uses the following includes: These files can be found at the following locations: -* -* -* - -Or, if you have installed the .NET 8 SDK on Windows: - -* `C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native` +- +- +- ### Step 2 - Initialize and start the .NET runtime diff --git a/docs/core/tutorials/publishing-with-visual-studio.md b/docs/core/tutorials/publishing-with-visual-studio.md index 6f23304a0571d..2bccb7fcce0e6 100644 --- a/docs/core/tutorials/publishing-with-visual-studio.md +++ b/docs/core/tutorials/publishing-with-visual-studio.md @@ -1,7 +1,8 @@ --- title: Publish a .NET console application using Visual Studio description: Learn how to use Visual Studio to create the set of files that are needed to run a .NET application. -ms.date: 08/22/2023 +ms.date: 10/23/2025 +ai-usage: ai-assisted ms.custom: - "vs-dotnet" - sfi-image-nochange @@ -12,7 +13,7 @@ This tutorial shows how to publish a console app so that other users can run it. ## Prerequisites -- This tutorial works with the console app that you create in [Create a .NET console application using Visual Studio](with-visual-studio.md). +This tutorial works with the console app that you create in [Create a .NET console application using Visual Studio](with-visual-studio.md). ## Publish the app diff --git a/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibrary/Class1.cs b/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibrary/Class1.cs index 2cce64ed81e23..036d643376f62 100644 --- a/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibrary/Class1.cs +++ b/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibrary/Class1.cs @@ -1,16 +1,12 @@ -using System; +namespace UtilityLibraries; -namespace UtilityLibraries +public static class StringLibrary { - public static class StringLibrary + public static bool StartsWithUpper(this string? str) { - public static bool StartsWithUpper(this string str) - { - if (string.IsNullOrWhiteSpace(str)) - return false; + if (string.IsNullOrWhiteSpace(str)) + return false; - char ch = str[0]; - return char.IsUpper(ch); - } + return char.IsUpper(str[0]); } } diff --git a/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTest/UnitTest1.cs b/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTest/UnitTest1.cs index 39c667fbaa0a8..81fb526c258d3 100644 --- a/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTest/UnitTest1.cs +++ b/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTest/UnitTest1.cs @@ -1,52 +1,45 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using UtilityLibraries; -namespace StringLibraryTest +namespace StringLibraryTest; + +[TestClass] +public class UnitTest1 { - [TestClass] - public class UnitTest1 + [TestMethod] + public void TestStartsWithUpper() { - [TestMethod] - public void TestStartsWithUpper() + // Tests that we expect to return true. + string[] words = ["Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"]; + foreach (var word in words) { - // Tests that we expect to return true. - string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" }; - foreach (var word in words) - { - bool result = word.StartsWithUpper(); - Assert.IsTrue(result, - string.Format("Expected for '{0}': true; Actual: {1}", - word, result)); - } + bool result = word.StartsWithUpper(); + Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}"); } + } - [TestMethod] - public void TestDoesNotStartWithUpper() + [TestMethod] + public void TestDoesNotStartWithUpper() + { + // Tests that we expect to return false. + string[] words = ["alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", + "1234", ".", ";", " "]; + foreach (var word in words) { - // Tests that we expect to return false. - string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", - "1234", ".", ";", " " }; - foreach (var word in words) - { - bool result = word.StartsWithUpper(); - Assert.IsFalse(result, - string.Format("Expected for '{0}': false; Actual: {1}", - word, result)); - } + bool result = word.StartsWithUpper(); + Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}"); } + } - [TestMethod] - public void DirectCallWithNullOrEmpty() + [TestMethod] + public void DirectCallWithNullOrEmpty() + { + // Tests that we expect to return false. + string?[] words = [string.Empty, null]; + foreach (var word in words) { - // Tests that we expect to return false. - string?[] words = { string.Empty, null }; - foreach (var word in words) - { - bool result = StringLibrary.StartsWithUpper(word); - Assert.IsFalse(result, - string.Format("Expected for '{0}': false; Actual: {1}", - word == null ? "" : word, result)); - } + bool result = StringLibrary.StartsWithUpper(word); + Assert.IsFalse(result, $"Expected for '{word ?? ""}': false; Actual: {result}"); } } } diff --git a/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTestNet8/UnitTest1.cs b/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTestNet8/UnitTest1.cs index 5d23b7e7ccb20..3a1e800acd120 100644 --- a/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTestNet8/UnitTest1.cs +++ b/docs/core/tutorials/snippets/library-with-visual-studio/csharp/StringLibraryTestNet8/UnitTest1.cs @@ -1,51 +1,44 @@ using UtilityLibraries; -namespace StringLibraryTest +namespace StringLibraryTest; + +[TestClass] +public class UnitTest1 { - [TestClass] - public class UnitTest1 + [TestMethod] + public void TestStartsWithUpper() { - [TestMethod] - public void TestStartsWithUpper() + // Tests that we expect to return true. + string[] words = ["Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"]; + foreach (var word in words) { - // Tests that we expect to return true. - string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" }; - foreach (var word in words) - { - bool result = word.StartsWithUpper(); - Assert.IsTrue(result, - string.Format("Expected for '{0}': true; Actual: {1}", - word, result)); - } + bool result = word.StartsWithUpper(); + Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}"); } + } - [TestMethod] - public void TestDoesNotStartWithUpper() + [TestMethod] + public void TestDoesNotStartWithUpper() + { + // Tests that we expect to return false. + string[] words = ["alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", + "1234", ".", ";", " "]; + foreach (var word in words) { - // Tests that we expect to return false. - string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", - "1234", ".", ";", " " }; - foreach (var word in words) - { - bool result = word.StartsWithUpper(); - Assert.IsFalse(result, - string.Format("Expected for '{0}': false; Actual: {1}", - word, result)); - } + bool result = word.StartsWithUpper(); + Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}"); } + } - [TestMethod] - public void DirectCallWithNullOrEmpty() + [TestMethod] + public void DirectCallWithNullOrEmpty() + { + // Tests that we expect to return false. + string?[] words = [string.Empty, null]; + foreach (var word in words) { - // Tests that we expect to return false. - string?[] words = { string.Empty, null }; - foreach (var word in words) - { - bool result = StringLibrary.StartsWithUpper(word); - Assert.IsFalse(result, - string.Format("Expected for '{0}': false; Actual: {1}", - word == null ? "" : word, result)); - } + bool result = StringLibrary.StartsWithUpper(word); + Assert.IsFalse(result, $"Expected for '{word ?? ""}': false; Actual: {result}"); } } } diff --git a/docs/core/tutorials/testing-library-with-visual-studio-code.md b/docs/core/tutorials/testing-library-with-visual-studio-code.md index c214581142e63..17774e3993743 100644 --- a/docs/core/tutorials/testing-library-with-visual-studio-code.md +++ b/docs/core/tutorials/testing-library-with-visual-studio-code.md @@ -1,7 +1,8 @@ --- title: Test a .NET class library using Visual Studio Code description: Learn how to use Visual Studio Code and run a unit test project for a .NET class library. -ms.date: 06/14/2024 +ms.date: 10/23/2025 +ai-usage: ai-assisted --- # Tutorial: Test a .NET class library using Visual Studio Code @@ -9,7 +10,7 @@ This tutorial shows how to automate unit testing by adding a test project to a s ## Prerequisites -- This tutorial works with the solution that you create in [Create a .NET class library using Visual Studio Code](library-with-visual-studio-code.md). +This tutorial works with the solution that you create in [Create a .NET class library using Visual Studio Code](library-with-visual-studio-code.md). ## Create a unit test project diff --git a/docs/core/tutorials/testing-library-with-visual-studio.md b/docs/core/tutorials/testing-library-with-visual-studio.md index c55e38684d86e..03d09c6e1b367 100644 --- a/docs/core/tutorials/testing-library-with-visual-studio.md +++ b/docs/core/tutorials/testing-library-with-visual-studio.md @@ -1,7 +1,8 @@ --- title: Test a .NET class library using Visual Studio description: Learn how to use Visual Studio to create and run a unit test project for a .NET class library. -ms.date: 08/23/2023 +ms.date: 10/23/2025 +ai-usage: ai-assisted dev_langs: - "csharp" - "vb" @@ -13,7 +14,7 @@ This tutorial shows how to automate unit testing by adding a test project to a s ## Prerequisites -- This tutorial works with the solution that you create in [Create a .NET class library using Visual Studio](library-with-visual-studio.md). +This tutorial works with the solution that you create in [Create a .NET class library using Visual Studio](library-with-visual-studio.md). ## Create a unit test project @@ -35,7 +36,7 @@ Unit tests provide automated software testing during your development and publis 1. On the **Additional information** page, select **.NET 8** in the **Framework** box. Then choose **Create**. -1. Visual Studio creates the project and opens the class file in the code window with the following code. If the language you want to use is not shown, change the language selector at the top of the page. +1. Visual Studio creates the project and opens the class file in the code window with the following code. If the language you want to use isn't shown, change the language selector at the top of the page. ```csharp namespace StringLibraryTest; @@ -66,11 +67,11 @@ Unit tests provide automated software testing during your development and publis The source code created by the unit test template does the following: - - It imports the namespace, which contains the types used for unit testing. In C#, the namespace is imported via a `global using` directive in *GlobalUsings.cs*. - - It applies the attribute to the `UnitTest1` class. - - It applies the attribute to define `TestMethod1` in C# or `TestSub` in Visual Basic. + - Imports the namespace, which contains the types used for unit testing. In C#, the namespace is imported via a `global using` directive in *GlobalUsings.cs*. + - Applies the attribute to the `UnitTest1` class. + - Applies the attribute to define `TestMethod1` in C# or `TestSub` in Visual Basic. - Each method tagged with [[TestMethod]](xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute) in a test class tagged with [[TestClass]](xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute) is executed automatically when the unit test is run. + Each method tagged with [[TestMethod]](xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute) in a test class tagged with [[TestClass]](xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute) executes automatically when the unit test runs. ## Add a project reference @@ -78,13 +79,13 @@ For the test project to work with the `StringLibrary` class, add a reference in 1. In **Solution Explorer**, right-click the **Dependencies** node of the **StringLibraryTest** project and select **Add Project Reference** from the context menu. -1. In the **Reference Manager** dialog, expand the **Projects** node, and select the box next to **StringLibrary**. Adding a reference to the `StringLibrary` assembly allows the compiler to find **StringLibrary** methods while compiling the **StringLibraryTest** project. +1. In the **Reference Manager** dialog, expand the **Projects** node, and select the box next to **StringLibrary**. Adding a reference to the `StringLibrary` assembly lets the compiler find **StringLibrary** methods while compiling the **StringLibraryTest** project. 1. Select **OK**. ## Add and run unit test methods -When Visual Studio runs a unit test, it executes each method that is marked with the attribute in a class that is marked with the attribute. A test method ends when the first failure is found or when all tests contained in the method have succeeded. +When Visual Studio runs a unit test, it executes each method marked with the attribute in a class marked with the attribute. A test method ends when the first failure is found or when all tests contained in the method succeed. The most common tests call members of the class. Many assert methods include at least two parameters, one of which is the expected test result and the other of which is the actual test result. Some of the `Assert` class's most frequently called methods are shown in the following table: @@ -97,11 +98,11 @@ The most common tests call members of the (or `Assert.Throws` and `Assert.ThrowsExactly` if using MSTest 3.8 and later) method in a test method to indicate the type of exception it's expected to throw. The test fails if the specified exception isn't thrown. -In testing the `StringLibrary.StartsWithUpper` method, you want to provide a number of strings that begin with an uppercase character. You expect the method to return `true` in these cases, so you can call the method. Similarly, you want to provide a number of strings that begin with something other than an uppercase character. You expect the method to return `false` in these cases, so you can call the method. +In testing the `StringLibrary.StartsWithUpper` method, you want to provide many strings that begin with an uppercase character. You expect the method to return `true` in these cases, so you can call the method. Similarly, you want to provide many strings that begin with something other than an uppercase character. You expect the method to return `false` in these cases, so you can call the method. -Since your library method handles strings, you also want to make sure that it successfully handles an [empty string (`String.Empty`)](xref:System.String.Empty), a valid string that has no characters and whose is 0, and a `null` string that hasn't been initialized. You can call `StartsWithUpper` directly as a static method and pass a single argument. Or you can call `StartsWithUpper` as an extension method on a `string` variable assigned to `null`. +Since your library method handles strings, you also want to make sure that it successfully handles an [empty string (`String.Empty`)](xref:System.String.Empty), a valid string that has no characters and whose is 0, and a `null` string that hasn't been initialized. Call `StartsWithUpper` directly as a static method and pass a single argument. Or call `StartsWithUpper` as an extension method on a `string` variable assigned to `null`. -You'll define three methods, each of which calls an method for each element in a string array. You'll call a method overload that lets you specify an error message to be displayed in case of test failure. The message identifies the string that caused the failure. +Define three methods, each of which calls an method for each element in a string array. Call a method overload that lets you specify an error message to be displayed in case of test failure. The message identifies the string that caused the failure. To create the test methods: @@ -122,7 +123,7 @@ To create the test methods: :::image type="content" source="./media/testing-library-with-visual-studio/advanced-save-options.png" alt-text="Visual Studio Advanced Save Options dialog"::: - If you fail to save your source code as a UTF8-encoded file, Visual Studio may save it as an ASCII file. When that happens, the runtime doesn't accurately decode the UTF8 characters outside of the ASCII range, and the test results won't be correct. + If you fail to save your source code as a UTF8-encoded file, Visual Studio might save it as an ASCII file. When that happens, the runtime doesn't accurately decode the UTF8 characters outside of the ASCII range, and the test results aren't correct. 1. On the menu bar, select **Test** > **Run All Tests**. If the **Test Explorer** window doesn't open, open it by choosing **Test** > **Test Explorer**. The three tests are listed in the **Passed Tests** section, and the **Summary** section reports the result of the test run. @@ -151,7 +152,7 @@ If you're doing test-driven development (TDD), you write tests first and they fa 1. Select the failed test, `TestDoesNotStartWith`. - The **Test Explorer** window displays the message produced by the assert: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Because of the failure, no strings in the array after "Error" were tested. + The **Test Explorer** window displays the message produced by the assert: "Assert.IsFalse failed. Expected for 'Error': false; actual: True." Because of the failure, the strings in the array after "Error" weren't tested. :::image type="content" source="./media/testing-library-with-visual-studio/failed-test-detail.png" alt-text="Test Explorer window showing the IsFalse assertion failure"::: @@ -159,7 +160,7 @@ If you're doing test-driven development (TDD), you write tests first and they fa ## Test the Release version of the library -Now that the tests have all passed when running the Debug build of the library, run the tests an additional time against the Release build of the library. A number of factors, including compiler optimizations, can sometimes produce different behavior between Debug and Release builds. +Now that the tests have all passed when running the Debug build of the library, run the tests another time against the Release build of the library. Some factors, including compiler optimizations, can sometimes produce different behavior between Debug and Release builds. To test the Release build: @@ -177,7 +178,7 @@ To test the Release build: If you're using Visual Studio as your IDE, you can use the same process shown in [Tutorial: Debug a .NET console application using Visual Studio](debugging-with-visual-studio.md) to debug code using your unit test project. Instead of starting the *ShowCase* app project, right-click the **StringLibraryTests** project, and select **Debug Tests** from the context menu. -Visual Studio starts the test project with the debugger attached. Execution will stop at any breakpoint you've added to the test project or the underlying library code. +Visual Studio starts the test project with the debugger attached. Execution stops at any breakpoint you've added to the test project or the underlying library code. ## Additional resources @@ -186,7 +187,7 @@ Visual Studio starts the test project with the debugger attached. Execution will ## Next steps -In this tutorial, you unit tested a class library. You can make the library available to others by publishing it to [NuGet](https://nuget.org) as a package. To learn how, follow a NuGet tutorial: +In this tutorial, you unit tested a class library. Make the library available to others by publishing it to [NuGet](https://nuget.org) as a package. To learn how, follow a NuGet tutorial: > [!div class="nextstepaction"] > [Create and publish a NuGet package using Visual Studio](/nuget/quickstart/create-and-publish-a-package-using-visual-studio?tabs=netcore-cli) diff --git a/docs/core/tutorials/testing-with-cli.md b/docs/core/tutorials/testing-with-cli.md index cf087dbe403de..e1c8c8624bcdd 100644 --- a/docs/core/tutorials/testing-with-cli.md +++ b/docs/core/tutorials/testing-with-cli.md @@ -1,7 +1,8 @@ --- title: Organizing and testing projects with the .NET CLI description: This tutorial explains how to organize and test .NET projects from the command line. -ms.date: 04/15/2022 +ms.date: 10/23/2025 +ai-usage: ai-assisted --- # Organizing and testing projects with the .NET CLI @@ -23,7 +24,7 @@ If you want to introduce new types into a console app, you can do so by adding f |__Program.cs ``` -However, this flat structure only works well when the size of your project is relatively small. Can you imagine what will happen if you add 20 types to the project? The project definitely wouldn't be easy to navigate and maintain with that many files littering the project's root directory. +However, this flat structure only works well when the size of your project is relatively small. Can you imagine what happens if you add 20 types to the project? The project definitely wouldn't be easy to navigate and maintain with that many files littering the project's root directory. To organize the project, create a new folder and name it *Models* to hold the type files. Place the type files into the *Models* folder: From 690503f844d7d3ca80e621f5c4cbd27f690d4b3d Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:18:37 -0700 Subject: [PATCH 5/5] Add publishing guidance for macOS applications (#49417) * Initial plan * Add Mac OS publishing documentation Co-authored-by: agocke <515774+agocke@users.noreply.github.com> * Update warning message about unsigned apps Co-authored-by: agocke <515774+agocke@users.noreply.github.com> * Fix macOS branding and add debugging entitlements Co-authored-by: agocke <515774+agocke@users.noreply.github.com> * Add links to publishing overview and Apple notarization docs Co-authored-by: agocke <515774+agocke@users.noreply.github.com> * Clarify Native AOT terminology and add links Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> * Reorganize signing section structure Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> * Apply suggestions from code review * Update docs/core/deploying/macos.md * Update docs/core/deploying/macos.md * Update docs/core/deploying/macos.md * Update docs/core/deploying/macos.md --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: agocke <515774+agocke@users.noreply.github.com> Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com> Co-authored-by: Jan Kotas --- docs/core/deploying/macos.md | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/core/deploying/macos.md diff --git a/docs/core/deploying/macos.md b/docs/core/deploying/macos.md new file mode 100644 index 0000000000000..fcf27c97210c8 --- /dev/null +++ b/docs/core/deploying/macos.md @@ -0,0 +1,47 @@ +--- +title: Publish .NET apps for macOS +description: Learn how to publish .NET applications for macOS, including signing, notarization, and app entitlements. +author: agocke +ms.author: angocke +ms.date: 10/22/2025 +ms.topic: how-to +ai-usage: ai-assisted +--- + +# Publish .NET apps for macOS + +Publishing .NET applications for macOS requires several additional steps compared to other platforms, due to Apple's security requirements. + +## Prerequisites + +Before you publish your .NET application for macOS, ensure you have the following: + +- **Apple Developer Account**: Needed for code signing and notarization. +- **Xcode Command Line Tools**: Provides `codesign`, `altool`, and other utilities. +- **.NET SDK**: Ensure you have the latest .NET SDK installed. + +## Produce your app using .NET SDK + +Use one of the methods described in the [.NET application publishing overview](/dotnet/core/deploying/) to produce an application. You can create either a framework-dependent or self-contained application. + +## Sign and notarize your app + +Use [Apple's developer documentation](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution) to sign and notarize the app native binaries. .NET creates a native *apphost* executable as the entry point for your app. This apphost must be signed and, if your app uses special capabilities, it must be assigned the appropriate **entitlements**. + +### Entitlements for apps not published as Native AOT + +For apps not published as [Native AOT](native-aot/index.md), the `com.apple.security.cs.allow-jit` entitlement is required. + +### Entitlements for apps published as Native AOT + +For apps published as [Native AOT](native-aot/index.md), no entitlements are required. + +### Optional entitlements for debugging and diagnostics + +The following entitlements enable additional debugging and diagnostic capabilities: + +- **`com.apple.security.get-task-allow`**: Needed for dump collection with `createdump` and `dotnet dump`. +- **`com.apple.security.cs.debugger`**: Needed to attach a debugger to the process. + +> [!WARNING] +> Failing to sign and notarize your app might result in the application crashing while executing a restricted operation.