diff --git a/docs/ai/get-started-app-chat-template.md b/docs/ai/get-started-app-chat-template.md index bd9aeea8051b9..0bb34aa5724c9 100644 --- a/docs/ai/get-started-app-chat-template.md +++ b/docs/ai/get-started-app-chat-template.md @@ -39,7 +39,7 @@ The architecture of the chat app is shown in the following diagram: - **User interface** - The application's chat interface is a [Blazor WebAssembly](/aspnet/core/blazor/) application. This interface is what accepts user queries, routes request to the application backend, and displays generated responses. - **Backend** - The application backend is an [ASP.NET Core Minimal API](/aspnet/core/fundamentals/minimal-apis/overview). The backend hosts the Blazor static web application and is what orchestrates the interactions among the different services. Services used in this application include: - - [**Azure Cognitive Search**](/azure/search/search-what-is-azure-search) – Indexes documents from the data stored in an Azure Storage Account. This makes the documents searchable using [vector search](/azure/search/search-get-started-vector) capabilities. + - [**Azure AI Search**](/azure/search/search-what-is-azure-search) – Indexes documents from the data stored in an Azure Storage Account. This makes the documents searchable using [vector search](/azure/search/search-get-started-vector) capabilities. - [**Azure OpenAI Service**](/azure/ai-services/openai/overview) – Provides the Large Language Models (LLM) to generate responses. [Semantic Kernel](/semantic-kernel/whatissk) is used in conjunction with the Azure OpenAI Service to orchestrate the more complex AI workflows. ## Cost diff --git a/docs/csharp/advanced-topics/interface-implementation/default-interface-methods-versions.md b/docs/csharp/advanced-topics/interface-implementation/default-interface-methods-versions.md index 1e895d38e7a0b..1f5a96d54f5aa 100644 --- a/docs/csharp/advanced-topics/interface-implementation/default-interface-methods-versions.md +++ b/docs/csharp/advanced-topics/interface-implementation/default-interface-methods-versions.md @@ -47,6 +47,9 @@ First, add the new method to the interface, including the body of the method: :::code language="csharp" source="./snippets/default-interface-members-versions/finished/customer-relationship/ICustomer.cs" id="SnippetLoyaltyDiscountVersionOne"::: +> [!NOTE] +> The preceding example uses `DateTime.Now.AddYears(-2)` for simplicity in this tutorial. Be aware that `DateTime` calculations can have edge cases with daylight saving time transitions and leap years. For production code, consider using UTC time or more robust date calculation approaches when precision is important. + The library author wrote a first test to check the implementation: :::code language="csharp" source="./snippets/default-interface-members-versions/finished/customer-relationship/Program.cs" id="SnippetTestDefaultImplementation"::: diff --git a/docs/csharp/asynchronous-programming/index.md b/docs/csharp/asynchronous-programming/index.md index be81a52d2bb17..05a310679437e 100644 --- a/docs/csharp/asynchronous-programming/index.md +++ b/docs/csharp/asynchronous-programming/index.md @@ -11,16 +11,16 @@ The task asynchronous programming model is analogous to how people give instruct 1. Pour a cup of coffee. 1. Heat a pan, then fry two eggs. -1. Fry three slices of bacon. +1. Cook three hash brown patties. 1. Toast two pieces of bread. 1. Spread butter and jam on the toast. 1. Pour a glass of orange juice. -If you have experience with cooking, you might complete these instructions **asynchronously**. You start warming the pan for eggs, then start frying the bacon. You put the bread in the toaster, then start cooking the eggs. At each step of the process, you start a task, and then transition to other tasks that are ready for your attention. +If you have experience with cooking, you might complete these instructions **asynchronously**. You start warming the pan for eggs, then start cooking the hash browns. You put the bread in the toaster, then start cooking the eggs. At each step of the process, you start a task, and then transition to other tasks that are ready for your attention. -Cooking breakfast is a good example of asynchronous work that isn't parallel. One person (or thread) can handle all the tasks. One person can make breakfast asynchronously by starting the next task before the previous task completes. Each cooking task progresses regardless of whether someone is actively watching the process. As soon as you start warming the pan for the eggs, you can begin frying the bacon. After the bacon starts to cook, you can put the bread in the toaster. +Cooking breakfast is a good example of asynchronous work that isn't parallel. One person (or thread) can handle all the tasks. One person can make breakfast asynchronously by starting the next task before the previous task completes. Each cooking task progresses regardless of whether someone is actively watching the process. As soon as you start warming the pan for the eggs, you can begin cooking the hash browns. After the hash browns start to cook, you can put the bread in the toaster. -For a parallel algorithm, you need multiple people who cook (or multiple threads). One person cooks the eggs, another fries the bacon, and so on. Each person focuses on their one specific task. Each person who is cooking (or each thread) is blocked synchronously waiting for the current task to complete: Bacon ready to flip, bread ready to pop up in toaster, and so on. +For a parallel algorithm, you need multiple people who cook (or multiple threads). One person cooks the eggs, another cooks the hash browns, and so on. Each person focuses on their one specific task. Each person who is cooking (or each thread) is blocked synchronously waiting for the current task to complete: Hash browns ready to flip, bread ready to pop up in toaster, and so on. :::image type="content" source="media/synchronous-breakfast.png" border="false" alt-text="Diagram that shows instructions for preparing breakfast as a list of seven sequential tasks completed in 30 minutes."::: @@ -42,12 +42,12 @@ You can start by updating the code so the thread doesn't block while tasks are r :::code language="csharp" source="snippets/index/AsyncBreakfast-V2/Program.cs" ID="SnippetMain"::: -The code updates the original method bodies of `FryEggs`, `FryBacon`, and `ToastBread` to return `Task`, `Task`, and `Task` objects, respectively. The updated method names include the "Async" suffix: `FryEggsAsync`, `FryBaconAsync`, and `ToastBreadAsync`. The `Main` method returns the `Task` object, although it doesn't have a `return` expression, which is by design. For more information, see [Evaluation of a void-returning async function](/dotnet/csharp/language-reference/language-specification/classes#15144-evaluation-of-a-void-returning-async-function). +The code updates the original method bodies of `FryEggs`, `FryHashBrowns`, and `ToastBread` to return `Task`, `Task`, and `Task` objects, respectively. The updated method names include the "Async" suffix: `FryEggsAsync`, `FryHashBrownsAsync`, and `ToastBreadAsync`. The `Main` method returns the `Task` object, although it doesn't have a `return` expression, which is by design. For more information, see [Evaluation of a void-returning async function](/dotnet/csharp/language-reference/language-specification/classes#15144-evaluation-of-a-void-returning-async-function). > [!NOTE] > The updated code doesn't yet take advantage of key features of asynchronous programming, which can result in shorter completion times. The code processes the tasks in roughly the same amount of time as the initial synchronous version. For the full method implementations, see the [final version of the code](#review-final-code) later in this article. -Let's apply the breakfast example to the updated code. The thread doesn't block while the eggs or bacon are cooking, but the code also doesn't start other tasks until the current work completes. You still put the bread in the toaster and stare at the toaster until the bread pops up, but you can now respond to interruptions. In a restaurant where multiple orders are placed, the cook can start a new order while another is already cooking. +Let's apply the breakfast example to the updated code. The thread doesn't block while the eggs or hash browns are cooking, but the code also doesn't start other tasks until the current work completes. You still put the bread in the toaster and stare at the toaster until the bread pops up, but you can now respond to interruptions. In a restaurant where multiple orders are placed, the cook can start a new order while another is already cooking. In the updated code, the thread working on the breakfast isn't blocked while waiting for any started task that's unfinished. For some applications, this change is all you need. You can enable your app to support user interaction while data downloads from the web. In other scenarios, you might want to start other tasks while waiting for the previous task to complete. @@ -55,7 +55,7 @@ In the updated code, the thread working on the breakfast isn't blocked while wai For most operations, you want to start several independent tasks immediately. As each task completes, you initiate other work that's ready to start. When you apply this methodology to the breakfast example, you can prepare breakfast more quickly. You also get everything ready close to the same time, so you can enjoy a hot breakfast. -The class and related types are classes you can use to apply this style of reasoning to tasks that are in progress. This approach enables you to write code that more closely resembles the way you create breakfast in real life. You start cooking the eggs, bacon, and toast at the same time. As each food item requires action, you turn your attention to that task, take care of the action, and then wait for something else that requires your attention. +The class and related types are classes you can use to apply this style of reasoning to tasks that are in progress. This approach enables you to write code that more closely resembles the way you create breakfast in real life. You start cooking the eggs, hash browns, and toast at the same time. As each food item requires action, you turn your attention to that task, take care of the action, and then wait for something else that requires your attention. In your code, you start a task and hold on to the object that represents the work. You use the `await` method on the task to delay acting on the work until the result is ready. @@ -69,9 +69,9 @@ Task eggsTask = FryEggsAsync(2); Egg eggs = await eggsTask; Console.WriteLine("Eggs are ready"); -Task baconTask = FryBaconAsync(3); -Bacon bacon = await baconTask; -Console.WriteLine("Bacon is ready"); +Task hashBrownTask = FryHashBrownsAsync(3); +HashBrown hashBrown = await hashBrownTask; +Console.WriteLine("Hash browns are ready"); Task toastTask = ToastBreadAsync(2); Toast toast = await toastTask; @@ -84,14 +84,14 @@ Console.WriteLine("Oj is ready"); Console.WriteLine("Breakfast is ready!"); ``` -These revisions don't help to get your breakfast ready any faster. The `await` expression is applied to all tasks as soon as they start. The next step is to move the `await` expressions for the bacon and eggs to the end of the method, before you serve the breakfast: +These revisions don't help to get your breakfast ready any faster. The `await` expression is applied to all tasks as soon as they start. The next step is to move the `await` expressions for the hash browns and eggs to the end of the method, before you serve the breakfast: ```csharp Coffee cup = PourCoffee(); Console.WriteLine("Coffee is ready"); Task eggsTask = FryEggsAsync(2); -Task baconTask = FryBaconAsync(3); +Task hashBrownTask = FryHashBrownsAsync(3); Task toastTask = ToastBreadAsync(2); Toast toast = await toastTask; @@ -103,17 +103,17 @@ Console.WriteLine("Oj is ready"); Egg eggs = await eggsTask; Console.WriteLine("Eggs are ready"); -Bacon bacon = await baconTask; -Console.WriteLine("Bacon is ready"); +HashBrown hashBrown = await hashBrownTask; +Console.WriteLine("Hash browns are ready"); Console.WriteLine("Breakfast is ready!"); ``` You now have an asynchronously prepared breakfast that takes about 20 minutes to prepare. The total cook time is reduced because some tasks run concurrently. -:::image type="content" source="media/asynchronous-breakfast.png" border="false" alt-text="Diagram that shows instructions for preparing breakfast as eight asynchronous tasks that complete in about 20 minutes, where unfortunately, the eggs and bacon burn."::: +:::image type="content" source="media/asynchronous-breakfast.png" border="false" alt-text="Diagram that shows instructions for preparing breakfast as eight asynchronous tasks that complete in about 20 minutes, where unfortunately, the eggs and hash browns burn."::: -The code updates improve the preparation process by reducing the cook time, but they introduce a regression by burning the eggs and bacon. You start all the asynchronous tasks at once. You wait on each task only when you need the results. The code might be similar to program in a web application that makes requests to different microservices and then combines the results into a single page. You make all the requests immediately, and then apply the `await` expression on all those tasks and compose the web page. +The code updates improve the preparation process by reducing the cook time, but they introduce a regression by burning the eggs and hash browns. You start all the asynchronous tasks at once. You wait on each task only when you need the results. The code might be similar to program in a web application that makes requests to different microservices and then combines the results into a single page. You make all the requests immediately, and then apply the `await` expression on all those tasks and compose the web page. ## Support composition with tasks @@ -169,22 +169,22 @@ After you make the code changes, run the application and check the output: Pouring coffee Coffee is ready Warming the egg pan... -putting 3 slices of bacon in the pan -Cooking first side of bacon... +putting 3 hash brown patties in the pan +Cooking first side of hash browns... Putting a slice of bread in the toaster Putting a slice of bread in the toaster Start toasting... Fire! Toast is ruined! -Flipping a slice of bacon -Flipping a slice of bacon -Flipping a slice of bacon -Cooking the second side of bacon... +Flipping a hash brown patty +Flipping a hash brown patty +Flipping a hash brown patty +Cooking the second side of hash browns... Cracking 2 eggs Cooking the eggs ... -Put bacon on plate +Put hash browns on plate Put eggs on plate Eggs are ready -Bacon is ready +Hash browns are ready Unhandled exception. System.InvalidOperationException: The toaster is on fire at AsyncBreakfast.Program.ToastBreadAsync(Int32 slices) in Program.cs:line 65 at AsyncBreakfast.Program.MakeToastWithButterAndJamAsync(Int32 number) in Program.cs:line 36 @@ -218,9 +218,9 @@ throw new InvalidOperationException("The toaster is on fire"); You can improve the series of `await` expressions at the end of the previous code by using methods of the `Task` class. One API is the method, which returns a object that completes when all the tasks in its argument list are complete. The following code demonstrates this method: ```csharp -await Task.WhenAll(eggsTask, baconTask, toastTask); +await Task.WhenAll(eggsTask, hashBrownTask, toastTask); Console.WriteLine("Eggs are ready"); -Console.WriteLine("Bacon is ready"); +Console.WriteLine("Hash browns are ready"); Console.WriteLine("Toast is ready"); Console.WriteLine("Breakfast is ready!"); ``` @@ -228,7 +228,7 @@ Console.WriteLine("Breakfast is ready!"); Another option is to use the method, which returns a `Task` object that completes when any of its arguments complete. You can wait on the returned task because you know the task is done. The following code shows how you can use the method to wait on the first task to finish and then process its result. After you process the result from the completed task, you remove the completed task from the list of tasks passed to the `WhenAny` method. ```csharp -var breakfastTasks = new List { eggsTask, baconTask, toastTask }; +var breakfastTasks = new List { eggsTask, hashBrownTask, toastTask }; while (breakfastTasks.Count > 0) { Task finishedTask = await Task.WhenAny(breakfastTasks); @@ -236,9 +236,9 @@ while (breakfastTasks.Count > 0) { Console.WriteLine("Eggs are ready"); } - else if (finishedTask == baconTask) + else if (finishedTask == hashBrownTask) { - Console.WriteLine("Bacon is ready"); + Console.WriteLine("Hash browns are ready"); } else if (finishedTask == toastTask) { diff --git a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V2/Program.cs b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V2/Program.cs index f1e537c70e4e0..729dddd2a3060 100644 --- a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V2/Program.cs +++ b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V2/Program.cs @@ -6,7 +6,7 @@ namespace AsyncBreakfast class Program { // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose. - internal class Bacon { } + internal class HashBrown { } internal class Coffee { } internal class Egg { } internal class Juice { } @@ -21,8 +21,8 @@ static async Task Main(string[] args) Egg eggs = await FryEggsAsync(2); Console.WriteLine("eggs are ready"); - Bacon bacon = await FryBaconAsync(3); - Console.WriteLine("bacon is ready"); + HashBrown hashBrown = await FryHashBrownsAsync(3); + Console.WriteLine("hash browns are ready"); Toast toast = await ToastBreadAsync(2); ApplyButter(toast); @@ -60,20 +60,20 @@ private static async Task ToastBreadAsync(int slices) return new Toast(); } - private static async Task FryBaconAsync(int slices) + private static async Task FryHashBrownsAsync(int patties) { - Console.WriteLine($"putting {slices} slices of bacon in the pan"); - Console.WriteLine("cooking first side of bacon..."); + Console.WriteLine($"putting {patties} hash brown patties in the pan"); + Console.WriteLine("cooking first side of hash browns..."); await Task.Delay(3000); - for (int slice = 0; slice < slices; slice++) + for (int patty = 0; patty < patties; patty++) { - Console.WriteLine("flipping a slice of bacon"); + Console.WriteLine("flipping a hash brown patty"); } - Console.WriteLine("cooking the second side of bacon..."); + Console.WriteLine("cooking the second side of hash browns..."); await Task.Delay(3000); - Console.WriteLine("Put bacon on plate"); + Console.WriteLine("Put hash browns on plate"); - return new Bacon(); + return new HashBrown(); } private static async Task FryEggsAsync(int howMany) diff --git a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V3/Program.cs b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V3/Program.cs index 7ceab2c2bb491..6aedcfe37540b 100644 --- a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V3/Program.cs +++ b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-V3/Program.cs @@ -6,7 +6,7 @@ namespace AsyncBreakfast class Program { // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose. - internal class Bacon { } + internal class HashBrown { } internal class Coffee { } internal class Egg { } internal class Juice { } @@ -19,14 +19,14 @@ static async Task Main(string[] args) Console.WriteLine("coffee is ready"); var eggsTask = FryEggsAsync(2); - var baconTask = FryBaconAsync(3); + var hashBrownTask = FryHashBrownsAsync(3); var toastTask = MakeToastWithButterAndJamAsync(2); var eggs = await eggsTask; Console.WriteLine("eggs are ready"); - var bacon = await baconTask; - Console.WriteLine("bacon is ready"); + var hashBrown = await hashBrownTask; + Console.WriteLine("hash browns are ready"); var toast = await toastTask; Console.WriteLine("toast is ready"); @@ -77,20 +77,20 @@ private static async Task ToastBreadAsync(int slices) return new Toast(); } - private static async Task FryBaconAsync(int slices) + private static async Task FryHashBrownsAsync(int patties) { - Console.WriteLine($"putting {slices} slices of bacon in the pan"); - Console.WriteLine("cooking first side of bacon..."); + Console.WriteLine($"putting {patties} hash brown patties in the pan"); + Console.WriteLine("cooking first side of hash browns..."); await Task.Delay(3000); - for (int slice = 0; slice < slices; slice++) + for (int patty = 0; patty < patties; patty++) { - Console.WriteLine("flipping a slice of bacon"); + Console.WriteLine("flipping a hash brown patty"); } - Console.WriteLine("cooking the second side of bacon..."); + Console.WriteLine("cooking the second side of hash browns..."); await Task.Delay(3000); - Console.WriteLine("Put bacon on plate"); + Console.WriteLine("Put hash browns on plate"); - return new Bacon(); + return new HashBrown(); } private static async Task FryEggsAsync(int howMany) diff --git a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-final/Program.cs b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-final/Program.cs index 55907a990a9a9..2d824a4d06378 100644 --- a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-final/Program.cs +++ b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-final/Program.cs @@ -5,7 +5,7 @@ namespace AsyncBreakfast { // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose. - internal class Bacon { } + internal class HashBrown { } internal class Coffee { } internal class Egg { } internal class Juice { } @@ -19,10 +19,10 @@ static async Task Main(string[] args) Console.WriteLine("coffee is ready"); var eggsTask = FryEggsAsync(2); - var baconTask = FryBaconAsync(3); + var hashBrownTask = FryHashBrownsAsync(3); var toastTask = MakeToastWithButterAndJamAsync(2); - var breakfastTasks = new List { eggsTask, baconTask, toastTask }; + var breakfastTasks = new List { eggsTask, hashBrownTask, toastTask }; while (breakfastTasks.Count > 0) { Task finishedTask = await Task.WhenAny(breakfastTasks); @@ -30,9 +30,9 @@ static async Task Main(string[] args) { Console.WriteLine("eggs are ready"); } - else if (finishedTask == baconTask) + else if (finishedTask == hashBrownTask) { - Console.WriteLine("bacon is ready"); + Console.WriteLine("hash browns are ready"); } else if (finishedTask == toastTask) { @@ -81,20 +81,20 @@ private static async Task ToastBreadAsync(int slices) return new Toast(); } - private static async Task FryBaconAsync(int slices) + private static async Task FryHashBrownsAsync(int patties) { - Console.WriteLine($"putting {slices} slices of bacon in the pan"); - Console.WriteLine("cooking first side of bacon..."); + Console.WriteLine($"putting {patties} hash brown patties in the pan"); + Console.WriteLine("cooking first side of hash browns..."); await Task.Delay(3000); - for (int slice = 0; slice < slices; slice++) + for (int patty = 0; patty < patties; patty++) { - Console.WriteLine("flipping a slice of bacon"); + Console.WriteLine("flipping a hash brown patty"); } - Console.WriteLine("cooking the second side of bacon..."); + Console.WriteLine("cooking the second side of hash browns..."); await Task.Delay(3000); - Console.WriteLine("Put bacon on plate"); + Console.WriteLine("Put hash browns on plate"); - return new Bacon(); + return new HashBrown(); } private static async Task FryEggsAsync(int howMany) diff --git a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-starter/Program.cs b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-starter/Program.cs index 6cbda6bba519c..96747fd9f1bd5 100644 --- a/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-starter/Program.cs +++ b/docs/csharp/asynchronous-programming/snippets/index/AsyncBreakfast-starter/Program.cs @@ -4,7 +4,7 @@ namespace AsyncBreakfast { // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose. - internal class Bacon { } + internal class HashBrown { } internal class Coffee { } internal class Egg { } internal class Juice { } @@ -20,8 +20,8 @@ static void Main(string[] args) Egg eggs = FryEggs(2); Console.WriteLine("eggs are ready"); - Bacon bacon = FryBacon(3); - Console.WriteLine("bacon is ready"); + HashBrown hashBrown = FryHashBrowns(3); + Console.WriteLine("hash browns are ready"); Toast toast = ToastBread(2); ApplyButter(toast); @@ -58,20 +58,20 @@ private static Toast ToastBread(int slices) return new Toast(); } - private static Bacon FryBacon(int slices) + private static HashBrown FryHashBrowns(int patties) { - Console.WriteLine($"putting {slices} slices of bacon in the pan"); - Console.WriteLine("cooking first side of bacon..."); + Console.WriteLine($"putting {patties} hash brown patties in the pan"); + Console.WriteLine("cooking first side of hash browns..."); Task.Delay(3000).Wait(); - for (int slice = 0; slice < slices; slice++) + for (int patty = 0; patty < patties; patty++) { - Console.WriteLine("flipping a slice of bacon"); + Console.WriteLine("flipping a hash brown patty"); } - Console.WriteLine("cooking the second side of bacon..."); + Console.WriteLine("cooking the second side of hash browns..."); Task.Delay(3000).Wait(); - Console.WriteLine("Put bacon on plate"); + Console.WriteLine("Put hash browns on plate"); - return new Bacon(); + return new HashBrown(); } private static Egg FryEggs(int howMany) diff --git a/docs/csharp/delegate-class.md b/docs/csharp/delegate-class.md index 089d7464d540a..c9c99a1503d2e 100644 --- a/docs/csharp/delegate-class.md +++ b/docs/csharp/delegate-class.md @@ -12,203 +12,158 @@ ms.assetid: f3742fda-13c2-4283-8966-9e21c2674393 This article covers the classes in .NET that support delegates, and how those map to the `delegate` keyword. +## What are delegates? + +Think of a delegate as a way to store a reference to a method, similar to how you might store a reference to an object. Just as you can pass objects to methods, you can pass method references using delegates. This is useful when you want to write flexible code where different methods can be "plugged in" to provide different behaviors. + +For example, imagine you have a calculator that can perform operations on two numbers. Instead of hard-coding addition, subtraction, multiplication, and division into separate methods, you could use delegates to represent any operation that takes two numbers and returns a result. + ## Define delegate types -Let's start with the 'delegate' keyword, because that's primarily what -you will use as you work with delegates. The code that the -compiler generates when you use the `delegate` keyword will -map to method calls that invoke members of the -and classes. +Now let's see how to create delegate types using the `delegate` keyword. When you define a delegate type, you're essentially creating a template that describes what kind of methods can be stored in that delegate. + +You define a delegate type using syntax that looks similar to a method signature, but with the `delegate` keyword at the beginning: -You define a delegate type using syntax that is similar to defining -a method signature. You just add the `delegate` keyword to the -definition. +```csharp +// Define a simple delegate that can point to methods taking two integers and returning an integer +public delegate int Calculator(int x, int y); +``` -Let's continue to use the List.Sort() method as our example. The first -step is to create a type for the comparison delegate: +This `Calculator` delegate can hold references to any method that takes two `int` parameters and returns an `int`. + +Let's look at a more practical example. When you want to sort a list, you need to tell the sorting algorithm how to compare items. Let's see how delegates help with the `List.Sort()` method. The first step is to create a delegate type for the comparison operation: ```csharp // From the .NET Core library - -// Define the delegate type: public delegate int Comparison(T left, T right); ``` -The compiler generates a class, derived from `System.Delegate` -that matches the signature used (in this case, a method that -returns an integer, and has two arguments). The type -of that delegate is `Comparison`. The `Comparison` delegate -type is a generic type. For more information, see [Generic classes and methods](./fundamentals/types/generics.md). +This `Comparison` delegate can hold references to any method that: -Notice that the syntax may appear as though it is declaring -a variable, but it is actually declaring a *type*. You can -define delegate types inside classes, directly inside namespaces, -or even in the global namespace. +- Takes two parameters of type `T` +- Returns an `int` (typically -1, 0, or 1 to indicate "less than", "equal to", or "greater than") + +When you define a delegate type like this, the compiler automatically generates a class derived from `System.Delegate` that matches your signature. This class handles all the complexity of storing and invoking the method references for you. + +The `Comparison` delegate type is a generic type, which means it can work with any type `T`. For more information about generics, see [Generic classes and methods](./fundamentals/types/generics.md). + +Notice that even though the syntax looks similar to declaring a variable, you're actually declaring a new *type*. You can define delegate types inside classes, directly inside namespaces, or even in the global namespace. > [!NOTE] > Declaring delegate types (or other types) directly in > the global namespace is not recommended. -The compiler also generates add and remove handlers for this new -type so that clients of this class can add and remove methods from an instance's -invocation list. The compiler will enforce that the signature -of the method being added or removed matches the signature -used when declaring the method. +The compiler also generates add and remove handlers for this new type so that clients of this class can add and remove methods from an instance's invocation list. The compiler enforces that the signature of the method being added or removed matches the signature used when declaring the delegate type. ## Declare instances of delegates -After defining the delegate, you can create an instance of that type. -Like all variables in C#, you cannot declare delegate instances directly -in a namespace, or in the global namespace. +After defining the delegate type, you can create instances (variables) of that type. Think of this as creating a "slot" where you can store a reference to a method. -```csharp -// inside a class definition: +Like all variables in C#, you cannot declare delegate instances directly in a namespace or in the global namespace. -// Declare an instance of that type: +```csharp +// Inside a class definition: public Comparison comparator; ``` -The type of the variable is `Comparison`, the delegate type - defined earlier. The name of the variable is `comparator`. +The type of this variable is `Comparison` (the delegate type you defined earlier), and the name of the variable is `comparator`. At this point, `comparator` doesn't point to any method yet—it's like an empty slot waiting to be filled. - That code snippet above declared a member variable inside a class. You can also - declare delegate variables that are local variables, or arguments to methods. +You can also declare delegate variables as local variables or method parameters, just like any other variable type. ## Invoke delegates -You invoke the methods that are in the invocation list of a delegate by calling -that delegate. Inside the `Sort()` method, the code will call the -comparison method to determine which order to place objects: +Once you have a delegate instance that points to a method, you can call (invoke) that method through the delegate. You invoke the methods that are in the invocation list of a delegate by calling that delegate as if it were a method. + +Here's how the `Sort()` method uses the comparison delegate to determine the order of objects: ```csharp int result = comparator(left, right); ``` -In the line above, the code *invokes* the method attached to the delegate. -You treat the variable as a method name, and invoke it using normal -method call syntax. +In this line, the code *invokes* the method attached to the delegate. You treat the delegate variable as if it were a method name and call it using normal method call syntax. -That line of code makes an unsafe assumption: There's no guarantee that -a target has been added to the delegate. If no targets have been attached, -the line above would cause a `NullReferenceException` to be thrown. The -idioms used to address this problem are more complicated than a simple -null-check, and are covered later in this [series](delegates-patterns.md). +However, this line of code makes an unsafe assumption: it assumes that a target method has been added to the delegate. If no methods have been attached, the line above would cause a `NullReferenceException` to be thrown. The patterns used to address this problem are more sophisticated than a simple null-check, and are covered later in this [series](delegates-patterns.md). ## Assign, add, and remove invocation targets -That's how a delegate type is defined, and how delegate instances -are declared and invoked. +Now you know how to define delegate types, declare delegate instances, and invoke delegates. But how do you actually connect a method to a delegate? This is where delegate assignment comes in. -Developers that want to use the `List.Sort()` method need to define -a method whose signature matches the delegate type definition, and -assign it to the delegate used by the sort method. This assignment -adds the method to the invocation list of that delegate object. +To use a delegate, you need to assign a method to it. The method you assign must have the same signature (same parameters and return type) as the delegate type defines. -Suppose you wanted to sort a list of strings by their length. Your -comparison function might be the following: +Let's see a practical example. Suppose you want to sort a list of strings by their length. You need to create a comparison method that matches the `Comparison` delegate signature: ```csharp private static int CompareLength(string left, string right) => left.Length.CompareTo(right.Length); ``` -The method is declared as a private method. That's fine. You may not -want this method to be part of your public interface. It can still -be used as the comparison method when attached to a delegate. The -calling code will have this method attached to the target list of -the delegate object, and can access it through that delegate. +This method takes two strings and returns an integer indicating which string is "greater" (longer in this case). The method is declared as private, which is perfectly fine. You don't need the method to be part of your public interface to use it with a delegate. -You create that relationship by passing that method to the -`List.Sort()` method: +Now you can pass this method to the `List.Sort()` method: ```csharp phrases.Sort(CompareLength); ``` -Notice that the method name is used, without parentheses. Using the method -as an argument tells the compiler to convert the method reference into a reference -that can be used as a delegate invocation target, and attach that method as -an invocation target. +Notice that you use the method name without parentheses. This tells the compiler to convert the method reference into a delegate that can be invoked later. The `Sort()` method will call your `CompareLength` method whenever it needs to compare two strings. -You could also have been explicit by declaring a variable of type -`Comparison` and doing an assignment: +You can also be more explicit by declaring a delegate variable and assigning the method to it: ```csharp Comparison comparer = CompareLength; phrases.Sort(comparer); ``` -In uses where the method being used as a delegate target is a small method, -it's common to use [lambda expression](language-reference/operators/lambda-expressions.md) syntax -to perform the assignment: +Both approaches accomplish the same thing. The first approach is more concise, while the second makes the delegate assignment more explicit. + +For simple methods, it's common to use [lambda expressions](language-reference/operators/lambda-expressions.md) instead of defining a separate method: ```csharp Comparison comparer = (left, right) => left.Length.CompareTo(right.Length); phrases.Sort(comparer); ``` -Using lambda expressions for delegate targets -is covered more in a [later section](delegates-patterns.md). +Lambda expressions provide a compact way to define simple methods inline. Using lambda expressions for delegate targets is covered in more detail in a [later section](delegates-patterns.md). -The Sort() example typically attaches a single target method to the -delegate. However, delegate objects do support invocation lists that -have multiple target methods attached to a delegate object. +The examples so far show delegates with a single target method. However, delegate objects can support invocation lists that have multiple target methods attached to a single delegate object. This capability is particularly useful for event handling scenarios. ## Delegate and MulticastDelegate classes -The language support described above provides the features -and support you'll typically need to work with delegates. These -features are built on two classes in the .NET Core -framework: and . - -The `System.Delegate` class and its single direct subclass, -`System.MulticastDelegate`, provide the framework support for -creating delegates, registering methods as delegate targets, -and invoking all methods that are registered as a delegate -target. - -Interestingly, the `System.Delegate` and `System.MulticastDelegate` -classes are not themselves delegate types. They do provide the -basis for all specific delegate types. That same language -design process mandated that you cannot declare a class that derives -from `Delegate` or `MulticastDelegate`. The C# language rules prohibit it. - -Instead, the C# compiler creates instances of a class derived from `MulticastDelegate` -when you use the C# language keyword to declare delegate types. - -This design has its roots in the first release of C# and .NET. One -goal for the design team was to ensure that the language enforced -type safety when using delegates. That meant ensuring that delegates -were invoked with the right type and number of arguments. And, that -any return type was correctly indicated at compile time. Delegates -were part of the 1.0 .NET release, which was before generics. - -The best way to enforce this type safety was for the compiler to -create the concrete delegate classes that represented the -method signature being used. - -Even though you cannot create derived classes directly, you will -use the methods defined on these classes. Let's go through -the most common methods that you will use when you work with delegates. - -The first, most important fact to remember is that every delegate you -work with is derived from `MulticastDelegate`. A multicast delegate means -that more than one method target can be invoked when invoking through -a delegate. The original design considered making a distinction between -delegates where only one target method could be attached and invoked, -and delegates where multiple target methods could be attached and -invoked. That distinction proved to be less useful in practice than -originally thought. The two different classes were already created, -and have been in the framework since its initial public release. - -The methods that you will use the most with delegates are `Invoke()` and -`BeginInvoke()` / `EndInvoke()`. `Invoke()` will invoke all the methods that -have been attached to a particular delegate instance. As you saw above, you -typically invoke delegates using the method call syntax on the delegate -variable. As you'll see [later in this series](delegates-patterns.md), -there are patterns that work directly with these methods. - -Now that you've seen the language syntax and the classes that support -delegates, let's examine how strongly typed delegates are used, created, and invoked. +Behind the scenes, the delegate features you've been using are built on two key classes in the .NET framework: and . You don't usually work with these classes directly, but they provide the foundation that makes delegates work. + +The `System.Delegate` class and its direct subclass `System.MulticastDelegate` provide the framework support for creating delegates, registering methods as delegate targets, and invoking all methods that are registered with a delegate. + +Here's an interesting design detail: `System.Delegate` and `System.MulticastDelegate` are not themselves delegate types that you can use. Instead, they serve as the base classes for all the specific delegate types you create. The C# language prevents you from directly inheriting from these classes—you must use the `delegate` keyword instead. + +When you use the `delegate` keyword to declare a delegate type, the C# compiler automatically creates a class derived from `MulticastDelegate` with your specific signature. + +### Why this design? + +This design has its roots in the first release of C# and .NET. The design team had several goals: + +1. **Type Safety**: The team wanted to ensure that the language enforced type safety when using delegates. This means ensuring that delegates are invoked with the correct type and number of arguments, and that return types are correctly verified at compile time. + +2. **Performance**: By having the compiler generate concrete delegate classes that represent specific method signatures, the runtime can optimize delegate invocations. + +3. **Simplicity**: Delegates were included in the 1.0 .NET release, which was before generics were introduced. The design needed to work within the constraints of the time. + +The solution was to have the compiler create the concrete delegate classes that match your method signatures, ensuring type safety while hiding the complexity from you. + +### Working with delegate methods + +Even though you can't create derived classes directly, you'll occasionally use methods defined on the `Delegate` and `MulticastDelegate` classes. Here are the most important ones to know about: + +Every delegate you work with is derived from `MulticastDelegate`. A "multicast" delegate means that more than one method target can be invoked when calling through a delegate. The original design considered making a distinction between delegates that could only invoke one method versus delegates that could invoke multiple methods. In practice, this distinction proved less useful than originally thought, so all delegates in .NET support multiple target methods. + +The most commonly used methods when working with delegates are: + +- `Invoke()`: Calls all the methods attached to the delegate +- `BeginInvoke()` / `EndInvoke()`: Used for asynchronous invocation patterns (though `async`/`await` is now preferred) + +In most cases, you won't call these methods directly. Instead, you'll use the method call syntax on the delegate variable, as shown in the examples above. However, as you'll see [later in this series](delegates-patterns.md), there are patterns that work directly with these methods. + +## Summary + +Now that you've seen how the C# language syntax maps to the underlying .NET classes, you're ready to explore how strongly typed delegates are used, created, and invoked in more complex scenarios. [Next](delegates-strongly-typed.md) diff --git a/docs/csharp/fundamentals/types/snippets/anonymous-types/Program.cs b/docs/csharp/fundamentals/types/snippets/anonymous-types/Program.cs index 86423bb785516..2b64b2cb05cda 100644 --- a/docs/csharp/fundamentals/types/snippets/anonymous-types/Program.cs +++ b/docs/csharp/fundamentals/types/snippets/anonymous-types/Program.cs @@ -8,7 +8,7 @@ namespace anonymous_types class Product { public string? Color {get;set;} - public decimal Price {get;set;} + public decimal Price {get;set;} public string? Name {get;set;} public string? Category {get;set;} public string? Size {get;set;} diff --git a/docs/csharp/language-reference/compiler-messages/cs0433.md b/docs/csharp/language-reference/compiler-messages/cs0433.md index 4bb79b2af13ba..fe64b64a11667 100644 --- a/docs/csharp/language-reference/compiler-messages/cs0433.md +++ b/docs/csharp/language-reference/compiler-messages/cs0433.md @@ -14,7 +14,7 @@ The type TypeName1 exists in both TypeName2 and TypeName3 Two different assemblies referenced in your application contain the same namespace and type, which produces ambiguity. - To resolve this error, use the alias feature of the ([**References**](../compiler-options/inputs.md#references)) compiler option or do not reference one of your assemblies. + To resolve this error, use the `extern alias` feature with project reference aliases or do not reference one of your assemblies. You can also use the alias feature of the ([**References**](../compiler-options/inputs.md#references)) compiler option when compiling directly with the C# compiler. This error can also occur if: @@ -27,7 +27,7 @@ This error can also occur if: ```csharp // CS0433_1.cs in CS0433_1.csproj -// or compile with: /target:library +// compile with: dotnet build or /target:library namespace TypeBindConflicts { public class AggPubImpAggPubImp { } @@ -38,14 +38,14 @@ namespace TypeBindConflicts ```csharp // CS0433_2.cs in CS0433_2.csproj -// or compile with: /target:library +// compile with: dotnet build or /target:library namespace TypeBindConflicts { public class AggPubImpAggPubImp { } } ``` - So, when consuming these two libraries (`CS0433_1.dll` and `CS0433_2.dll`) in the project, using the `AggPubImpAddPubImp` type will be ambiguous and will lead to compiler error `CS0433`. + So, when consuming these two libraries (`CS0433_1.dll` and `CS0433_2.dll`) in the project, using the `AggPubImpAggPubImp` type will be ambiguous and will lead to compiler error `CS0433`. ```xml @@ -55,7 +55,7 @@ namespace TypeBindConflicts ```csharp // CS0433_3.cs in CS0433_3.csproj -// or compile with: /reference:cs0433_1.dll /reference:cs0433_2.dll +// compile with: dotnet build or /reference:cs0433_1.dll /reference:cs0433_2.dll using TypeBindConflicts; public class Test @@ -67,7 +67,11 @@ public class Test } ``` - The following example shows how you can use the alias feature of the **/reference** compiler option or `` feature in `` to resolve this CS0433 error. + The following example shows how you can use the `extern alias` feature with project references to resolve this CS0433 error. This is the recommended approach for modern .NET projects. + +### Step 1: Add an alias to one of the project references + +First, modify your project file to add an alias to one of the conflicting project references: ```xml @@ -76,10 +80,14 @@ public class Test ``` + +### Step 2: Use the extern alias in your code + +Then, use the `extern alias` directive and qualified type names to distinguish between the two types: ```csharp // CS0433_4.cs in CS0433_4.csproj -// compile with: /reference:cs0433_1.dll /reference:CustomTypes=cs0433_2.dll +// compile with: dotnet build or /reference:cs0433_1.dll /reference:CustomTypes=cs0433_2.dll extern alias CustomTypes; using TypeBindConflicts; @@ -87,10 +95,10 @@ public class Test { public static void Main() { - // AggPubImpAggPubImp taken from CS0433_1.dll + // AggPubImpAggPubImp taken from CS0433_2.dll (no alias, default global namespace) AggPubImpAggPubImp n6 = new AggPubImpAggPubImp(); - // AggPubImpAggPubImp taken from CS0433_2.dll + // AggPubImpAggPubImp taken from CS0433_1.dll (via CustomTypes alias) CustomTypes.TypeBindConflicts.AggPubImpAggPubImp n7 = new CustomTypes.TypeBindConflicts.AggPubImpAggPubImp(); } diff --git a/docs/csharp/language-reference/compiler-messages/nullable-warnings.md b/docs/csharp/language-reference/compiler-messages/nullable-warnings.md index 87cb8b4ce4da8..0d35943a6ce72 100644 --- a/docs/csharp/language-reference/compiler-messages/nullable-warnings.md +++ b/docs/csharp/language-reference/compiler-messages/nullable-warnings.md @@ -215,6 +215,32 @@ The following warnings indicate that you haven't set the nullable context correc - **CS8636** - *Invalid option for `/nullable`; must be `disable`, `enable`, `warnings` or `annotations`* - **CS8637** - *Expected `enable`, `disable`, or `restore`* +To set the nullable context correctly, you have two options: + +1. **Project-level configuration**: Add the [``](../compiler-options/language.md#nullable) element to your project file: + + ```xml + + enable + + ``` + +2. **File-level configuration**: Use [`#nullable`](../preprocessor-directives.md#nullable-context) preprocessor directives in your source code: + + ```csharp + #nullable enable + ``` + +The nullable context has two independent flags that control different aspects: + +- **Annotation flag**: Controls whether you can use `?` to declare nullable reference types and `!` to surpress individual warnings. +- **Warning flag**: Controls whether the compiler emits nullability warnings + +For detailed information about nullable contexts and migration strategies, see: + +- [Nullable reference types overview](../../nullable-references.md#nullable-context) +- [Update a codebase with nullable reference types](../../nullable-migration-strategies.md) + ## Incorrect annotation syntax These errors and warnings indicate that usage of the `!` or `?` annotation is incorrect. diff --git a/docs/csharp/language-reference/keywords/private-protected.md b/docs/csharp/language-reference/keywords/private-protected.md index e4a25999a1f6e..afd1462f87ff9 100644 --- a/docs/csharp/language-reference/keywords/private-protected.md +++ b/docs/csharp/language-reference/keywords/private-protected.md @@ -18,6 +18,8 @@ The `private protected` keyword combination is a member access modifier. A priva A private protected member of a base class is accessible from derived types in its containing assembly only if the static type of the variable is the derived class type. For example, consider the following code segment: ```csharp +// Assembly1.cs +// Compile with: /target:library public class BaseClass { private protected int myValue = 0; @@ -54,12 +56,26 @@ class DerivedClass2 : BaseClass ``` This example contains two files, `Assembly1.cs` and `Assembly2.cs`. -The first file contains a public base class, `BaseClass`, and a type derived from it, `DerivedClass1`. `BaseClass` owns a private protected member, `myValue`, which `DerivedClass1` tries to access in two ways. The first attempt to access `myValue` through an instance of `BaseClass` will produce an error. However, the attempt to use it as an inherited member in `DerivedClass1` will succeed. +The first file contains a public base class, `BaseClass`, and a type derived from it, `DerivedClass1`. `BaseClass` owns a private protected member, `myValue`, which `DerivedClass1` can access as an inherited member within the same assembly. -In the second file, an attempt to access `myValue` as an inherited member of `DerivedClass2` will produce an error, as it is only accessible by derived types in Assembly1. +In the second file, an attempt to access `myValue` as an inherited member of `DerivedClass2` will produce an error, because `private protected` members are only accessible by derived types **within the same assembly**. This is the key difference from `protected` (which allows access from derived classes in any assembly) and `protected internal` (which allows access from any class within the same assembly or derived classes in any assembly). If `Assembly1.cs` contains an that names `Assembly2`, the derived class `DerivedClass2` will have access to `private protected` members declared in `BaseClass`. `InternalsVisibleTo` makes `private protected` members visible to derived classes in other assemblies. +## Comparison with other protected access modifiers + +The following table summarizes the key differences between the three protected access modifiers: + +| Access Modifier | Same Assembly, Derived Class | Same Assembly, Non-derived Class | Different Assembly, Derived Class | +|---|:-:|:-:|:-:| +| `protected` | ✔️ | ❌ | ✔️ | +| `protected internal` | ✔️ | ✔️ | ✔️ | +| `private protected` | ✔️ | ❌ | ❌ | + +- Use `protected` when you want derived classes in any assembly to access the member +- Use `protected internal` when you want the most permissive access (any class in same assembly OR derived classes anywhere) +- Use `private protected` when you want the most restrictive protected access (only derived classes in the same assembly) + Struct members cannot be `private protected` because the struct cannot be inherited. ## C# language specification diff --git a/docs/csharp/language-reference/keywords/protected-internal.md b/docs/csharp/language-reference/keywords/protected-internal.md index 77ca6fb9aae29..9dae3cdc32e6a 100644 --- a/docs/csharp/language-reference/keywords/protected-internal.md +++ b/docs/csharp/language-reference/keywords/protected-internal.md @@ -53,8 +53,8 @@ class DerivedClass : BaseClass ``` This example contains two files, `Assembly1.cs` and `Assembly2.cs`. -The first file contains a public base class, `BaseClass`, and another class, `TestAccess`. `BaseClass` owns a protected internal member, `myValue`, which is accessed by the `TestAccess` type. -In the second file, an attempt to access `myValue` through an instance of `BaseClass` will produce an error, while an access to this member through an instance of a derived class, `DerivedClass` will succeed. +The first file contains a public base class, `BaseClass`, and another class, `TestAccess`. `BaseClass` owns a protected internal member, `myValue`, which is accessed by the `TestAccess` type because they're in the same assembly. +In the second file, an attempt to access `myValue` through an instance of `BaseClass` will produce an error, while an access to this member through an instance of a derived class, `DerivedClass` will succeed. This shows that `protected internal` allows access from **any class within the same assembly** or **derived classes in any assembly**, making it the most permissive of the protected access modifiers. Struct members cannot be `protected internal` because the struct cannot be inherited. diff --git a/docs/csharp/language-reference/keywords/protected.md b/docs/csharp/language-reference/keywords/protected.md index b81bbf41809d1..df8f11a27c708 100644 --- a/docs/csharp/language-reference/keywords/protected.md +++ b/docs/csharp/language-reference/keywords/protected.md @@ -24,9 +24,11 @@ For a comparison of `protected` with the other access modifiers, see [Accessibil A protected member of a base class is accessible in a derived class only if the access occurs through the derived class type. For example, consider the following code segment: -[!code-csharp[csrefKeywordsModifiers#11](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs#11)] +:::code language="csharp" source="./snippets/protected/Example1.cs" id="snippet1"::: -The statement `a.x = 10` generates an error because it accesses the protected member through a base class reference (`a` is of type `A`). Protected members can only be accessed through the derived class type or types derived from it. +The statement `baseObject.myValue = 10` generates an error because it accesses the protected member through a base class reference (`baseObject` is of type `BaseClass`). Protected members can only be accessed through the derived class type or types derived from it. + +Unlike `private protected`, the `protected` access modifier allows access from derived classes **in any assembly**. Unlike `protected internal`, it does **not** allow access from non-derived classes within the same assembly. Struct members cannot be protected because the struct cannot be inherited. @@ -34,7 +36,7 @@ Struct members cannot be protected because the struct cannot be inherited. In this example, the class `DerivedPoint` is derived from `Point`. Therefore, you can access the protected members of the base class directly from the derived class. -[!code-csharp[csrefKeywordsModifiers#12](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs#12)] +:::code language="csharp" source="./snippets/protected/Example2.cs" id="snippet1"::: If you change the access levels of `x` and `y` to [private](private.md), the compiler will issue the error messages: @@ -42,6 +44,16 @@ If you change the access levels of `x` and `y` to [private](private.md), the com `'Point.x' is inaccessible due to its protection level.` +## Cross-assembly access + +The following example demonstrates that `protected` members are accessible from derived classes even when they're in different assemblies: + +:::code language="csharp" source="./snippets/protected/Assembly1.cs" id="snippet1"::: + +:::code language="csharp" source="./snippets/protected/Assembly2.cs" id="snippet1"::: + +This cross-assembly accessibility is what distinguishes `protected` from `private protected` (which restricts access to the same assembly) but is similar to `protected internal` (though `protected internal` also allows same-assembly access from non-derived classes). + ## C# language specification For more information, see [Declared accessibility](~/_csharpstandard/standard/basic-concepts.md#752-declared-accessibility) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. diff --git a/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs b/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs index 49b66327a7f7f..02c597fe478c0 100644 --- a/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs +++ b/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs @@ -86,14 +86,32 @@ private static void OutVariableDeclaration() private static void FirstInExample() { // - int readonlyArgument = 44; - InArgExample(readonlyArgument); - Console.WriteLine(readonlyArgument); // value is still 44 - - void InArgExample(in int number) + var largeStruct = new LargeStruct { Value1 = 42, Value2 = 3.14, Value3 = "Hello" }; + + // Using 'in' avoids copying the large struct and prevents modification + ProcessLargeStruct(in largeStruct); + Console.WriteLine($"Original value unchanged: {largeStruct.Value1}"); + + // Without 'in', the struct would be copied (less efficient for large structs) + ProcessLargeStructByValue(largeStruct); + Console.WriteLine($"Original value still unchanged: {largeStruct.Value1}"); + + void ProcessLargeStruct(in LargeStruct data) { + // Can read the values + Console.WriteLine($"Processing: {data.Value1}, {data.Value2}, {data.Value3}"); + // Uncomment the following line to see error CS8331 - //number = 19; + // data.Value1 = 99; // Compilation error: cannot assign to 'in' parameter + } + + void ProcessLargeStructByValue(LargeStruct data) + { + // This method receives a copy of the struct + Console.WriteLine($"Processing copy: {data.Value1}, {data.Value2}, {data.Value3}"); + + // Modifying the copy doesn't affect the original + data.Value1 = 99; } // } @@ -111,6 +129,15 @@ public struct OptionStruct { // taking the place of lots of fields } + + public struct LargeStruct + { + public int Value1; + public double Value2; + public string Value3; + // In a real scenario, this struct might have many more fields + // making copying expensive + } // public class Book diff --git a/docs/csharp/language-reference/keywords/snippets/protected/Assembly1.cs b/docs/csharp/language-reference/keywords/snippets/protected/Assembly1.cs new file mode 100644 index 0000000000000..0b7bed2d6f86b --- /dev/null +++ b/docs/csharp/language-reference/keywords/snippets/protected/Assembly1.cs @@ -0,0 +1,11 @@ +// +// Assembly1.cs +// Compile with: /target:library +namespace Assembly1 +{ + public class BaseClass + { + protected int myValue = 0; + } +} +// \ No newline at end of file diff --git a/docs/csharp/language-reference/keywords/snippets/protected/Assembly2.cs b/docs/csharp/language-reference/keywords/snippets/protected/Assembly2.cs new file mode 100644 index 0000000000000..8917c01a496a0 --- /dev/null +++ b/docs/csharp/language-reference/keywords/snippets/protected/Assembly2.cs @@ -0,0 +1,18 @@ +// +// Assembly2.cs +// Compile with: /reference:Assembly1.dll +namespace Assembly2 +{ + using Assembly1; + + class DerivedClass : BaseClass + { + void Access() + { + // OK, because protected members are accessible from + // derived classes in any assembly + myValue = 10; + } + } +} +// \ No newline at end of file diff --git a/docs/csharp/language-reference/keywords/snippets/protected/Example1.cs b/docs/csharp/language-reference/keywords/snippets/protected/Example1.cs new file mode 100644 index 0000000000000..ec44b453301c5 --- /dev/null +++ b/docs/csharp/language-reference/keywords/snippets/protected/Example1.cs @@ -0,0 +1,25 @@ +// +namespace Example1 +{ + class BaseClass + { + protected int myValue = 123; + } + + class DerivedClass : BaseClass + { + static void Main() + { + var baseObject = new BaseClass(); + var derivedObject = new DerivedClass(); + + // Error CS1540, because myValue can only be accessed through + // the derived class type, not through the base class type. + // baseObject.myValue = 10; + + // OK, because this class derives from BaseClass. + derivedObject.myValue = 10; + } + } +} +// \ No newline at end of file diff --git a/docs/csharp/language-reference/keywords/snippets/protected/Example2.cs b/docs/csharp/language-reference/keywords/snippets/protected/Example2.cs new file mode 100644 index 0000000000000..90f37f99c1214 --- /dev/null +++ b/docs/csharp/language-reference/keywords/snippets/protected/Example2.cs @@ -0,0 +1,24 @@ +// +namespace Example2 +{ + class Point + { + protected int x; + protected int y; + } + + class DerivedPoint: Point + { + static void Main() + { + var dpoint = new DerivedPoint(); + + // Direct access to protected members. + dpoint.x = 10; + dpoint.y = 15; + Console.WriteLine($"x = {dpoint.x}, y = {dpoint.y}"); + } + } + // Output: x = 10, y = 15 +} +// \ No newline at end of file diff --git a/docs/csharp/language-reference/keywords/volatile.md b/docs/csharp/language-reference/keywords/volatile.md index ed729faf67a9e..121e285819641 100644 --- a/docs/csharp/language-reference/keywords/volatile.md +++ b/docs/csharp/language-reference/keywords/volatile.md @@ -13,6 +13,9 @@ ms.assetid: 78089bc7-7b38-4cfd-9e49-87ac036af009 The `volatile` keyword indicates that a field might be modified by multiple threads that are executing at the same time. The compiler, the runtime system, and even hardware may rearrange reads and writes to memory locations for performance reasons. Fields that are declared `volatile` are excluded from certain kinds of optimizations. There is no guarantee of a single total ordering of volatile writes as seen from all threads of execution. For more information, see the class. +> [!CAUTION] +> The `volatile` keyword is often misunderstood and misused in multithreaded programming. In most scenarios, you should use safer and more reliable alternatives instead of `volatile`. Modern .NET provides better concurrency tools like the class, the [`lock`](../statements/lock.md) statement, or higher-level synchronization primitives. These alternatives provide clearer semantics and stronger guarantees than `volatile`. Consider using `volatile` only in rare, advanced scenarios where you fully understand its limitations and have verified it's the appropriate solution. + > [!NOTE] > On a multiprocessor system, a volatile read operation does not guarantee to obtain the latest value written to that memory location by any processor. Similarly, a volatile write operation does not guarantee that the value written would be immediately visible to other processors. @@ -27,8 +30,21 @@ The `volatile` keyword can be applied to fields of these types: Other types, including `double` and `long`, cannot be marked `volatile` because reads and writes to fields of those types cannot be guaranteed to be atomic. To protect multi-threaded access to those types of fields, use the class members or protect access using the [`lock`](../statements/lock.md) statement. +For most multithreaded scenarios, even with supported types, prefer using operations, [`lock`](../statements/lock.md) statements, or other synchronization primitives instead of `volatile`. These alternatives are less prone to subtle concurrency bugs. + The `volatile` keyword can only be applied to fields of a `class` or `struct`. Local variables cannot be declared `volatile`. +## Alternatives to volatile + +In most cases, you should use one of these safer alternatives instead of `volatile`: + +- ** operations**: Provide atomic operations for numeric types and reference assignments. These are generally faster and provide stronger guarantees than `volatile`. +- **[`lock` statement](../statements/lock.md)**: Provides mutual exclusion and memory barriers. Use for protecting larger critical sections. +- ** class**: Provides explicit volatile read and write operations with clearer semantics than the `volatile` keyword. +- **Higher-level synchronization primitives**: Such as , , or concurrent collections from . + +The `volatile` keyword doesn't provide atomicity for operations other than assignment, doesn't prevent race conditions, and doesn't provide ordering guarantees for other memory operations. These limitations make it unsuitable for most concurrency scenarios. + ## Example The following example shows how to declare a public field variable as `volatile`. @@ -52,3 +68,6 @@ With the `volatile` modifier added to the declaration of `_shouldStop` in place, - [Modifiers](index.md) - [lock statement](../statements/lock.md) - +- +- +- [Managed Threading](../../../standard/threading/managed-threading-basics.md) diff --git a/docs/csharp/misc/cs0119.md b/docs/csharp/misc/cs0119.md index 2292054412a43..1d7558da70bf6 100644 --- a/docs/csharp/misc/cs0119.md +++ b/docs/csharp/misc/cs0119.md @@ -20,7 +20,9 @@ ms.assetid: 048924f1-378f-4021-bd20-299d3218f810 - A method identifier is used as if it were a struct or class -## Example +## Examples + +### Example 1 The following sample generates CS0119: 'C.B()' is a method, which is not valid in the given context. You can fix this error by changing the name of the method `C.B`, or using the fully qualified name for the class `B` like `N2.B`. @@ -42,3 +44,70 @@ namespace N1 } } ``` + +### Example 2 + +The following sample shows a common scenario where CS0119 occurs when trying to add class names to a collection of types. The compiler expects instances, not class names. + +```csharp +using System; +using System.Collections.Generic; + +public class Page_BaseClass { } +public class Page_CRTable { } +public class Page_DBGridWithTools { } + +public static class PageTypeManager +{ + public static List GetPageTypes() + { + List pageTypesList = new List(); + + // CS0119: 'Page_BaseClass' is a type, which is not valid in the given context + pageTypesList.Add(Page_BaseClass); + pageTypesList.Add(Page_CRTable); + pageTypesList.Add(Page_DBGridWithTools); + + return pageTypesList; + } +} +``` + +To fix this error, use the method or the `typeof` operator to get `Type` instances: + +```csharp +using System; +using System.Collections.Generic; + +public static class PageTypeManager +{ + public static List GetPageTypes() + { + List pageTypesList = new List(); + + // Use typeof operator to get Type instances + pageTypesList.Add(typeof(Page_BaseClass)); + pageTypesList.Add(typeof(Page_CRTable)); + pageTypesList.Add(typeof(Page_DBGridWithTools)); + + return pageTypesList; + } + + // Alternative: Load types dynamically by name + public static List GetPageTypesByName(string[] typeNames) + { + List pageTypesList = new List(); + + foreach (string typeName in typeNames) + { + Type type = Type.GetType(typeName); + if (type != null) + { + pageTypesList.Add(type); + } + } + + return pageTypesList; + } +} +``` diff --git a/docs/csharp/misc/cs0236.md b/docs/csharp/misc/cs0236.md index 3d7f519d4d605..4a90b6fb503f9 100644 --- a/docs/csharp/misc/cs0236.md +++ b/docs/csharp/misc/cs0236.md @@ -13,9 +13,27 @@ A field initializer cannot reference the non-static field, method, or property ' Instance fields cannot be used to initialize other instance fields outside a method. +## Why this error occurs + +The compiler enforces this restriction because of how object initialization works in C#. When an object is created, field initializers are processed before any constructor code runs. During this phase: + +1. **Field dependency restriction**: While field initializers are processed in lexical order within a single file, the rule prevents dependencies between fields to allow class authors to rearrange fields without introducing compiler errors. For partial classes, the order of field initializers across different source files isn't specified. + +2. **Object is not fully constructed**: When field initializers run, the object instance is in an incomplete state. Allowing references between fields during this phase could lead to accessing uninitialized memory or unpredictable behavior. + +3. **Compiler safety**: This restriction prevents potential runtime errors and ensures predictable object construction behavior. + +The compiler detects this pattern during compilation and reports CS0236 to prevent these potential issues. + ## To correct this error -If you are trying to initialize a variable outside a method, consider performing the initialization inside the class constructor. For more information, see [Methods](../programming-guide/classes-and-structs/methods.md). +1. **Move initialization to constructor**: Perform the initialization inside an instance constructor where all fields are guaranteed to be available. + +2. **Use static fields**: If the referenced field doesn't need to be instance-specific, consider making it static. + +3. **Use default values**: Initialize fields with literal values or expressions that don't reference other instance members. + +4. **Lazy initialization**: Use properties with backing fields for complex initialization logic that depends on other instance members. ## Example @@ -26,13 +44,47 @@ public class MyClass { public int i = 5; - // To fix the error, remove "= i", and uncomment the line in constructor. + // CS0236: Field initializer cannot reference instance field 'i' + // This restriction allows class authors to rearrange fields without compiler errors public int j = i; // CS0236 public MyClass() { - // Uncomment the following. - //j = i; + // This works because both fields are guaranteed to be initialized + // before constructor code runs + j = i; + } +} +``` + +## Additional examples + +The following examples demonstrate different scenarios where CS0236 occurs: + +```csharp +public class Examples +{ + private string name = "Default"; + + // CS0236: Cannot reference instance field in initializer + private string displayName = name.ToUpper(); + + // CS0236: Cannot reference instance method in initializer + private int length = GetNameLength(); + + // CS0236: Cannot reference instance property in initializer + private string formatted = FormattedName; + + public string FormattedName => $"Name: {name}"; + + private int GetNameLength() => name.Length; + + public Examples() + { + // All of these work in the constructor: + displayName = name.ToUpper(); + length = GetNameLength(); + formatted = FormattedName; } } ``` diff --git a/docs/csharp/tutorials/console-teleprompter.md b/docs/csharp/tutorials/console-teleprompter.md index bc51f930eedad..6f8ea634ad5e2 100644 --- a/docs/csharp/tutorials/console-teleprompter.md +++ b/docs/csharp/tutorials/console-teleprompter.md @@ -25,7 +25,22 @@ There are a lot of features in this tutorial. Let's build them one by one. ## Create the app -The first step is to create a new application. Open a command prompt and create a new directory for your application. Make that the current directory. Type the command `dotnet new console` at the command prompt. This creates the starter files for a basic "Hello World" application. +The first step is to create a new application. Open a command prompt and create a new directory for your application. Make that the current directory. Type the command `dotnet new console` at the command prompt. For example: + +```console +E:\development\VSprojects>mkdir teleprompter +E:\development\VSprojects>cd teleprompter +E:\development\VSprojects\teleprompter>dotnet new console +The template "Console Application" was created successfully. + +Processing post-creation actions... +Running 'dotnet restore' on E:\development\VSprojects\teleprompter\teleprompter.csproj... + Determining projects to restore... + Restored E:\development\VSprojects\teleprompter\teleprompter.csproj (in 78 ms). +Restore succeeded. +``` + +This creates the starter files for a basic "Hello World" application. Before you start making modifications, let's run the simple Hello World application. After creating the application, type `dotnet run` at the command prompt. This command runs the NuGet package restore process, creates the application executable, and runs the executable. @@ -201,11 +216,11 @@ private static async Task GetInput() This creates a lambda expression to represent an delegate that reads a key from the Console and modifies a local variable representing the delay when the user presses the '<' (less than) or '>' (greater than) keys. The delegate method finishes when user presses the 'X' or 'x' keys, which allow the user to stop the text display at any time. This method uses to block and wait for the user to press a key. -To finish this feature, you need to create a new `async Task` returning method that starts both of these tasks (`GetInput` and `ShowTeleprompter`), and also manages the shared data between these two tasks. - It's time to create a class that can handle the shared data between these two tasks. This class contains two public properties: the delay, and a flag `Done` to indicate that the file has been completely read: ```csharp +using static System.Math; + namespace TeleprompterConsole; internal class TelePrompterConfig @@ -225,17 +240,13 @@ internal class TelePrompterConfig } ``` -Put that class in a new file, and include that class in the -`TeleprompterConsole` namespace as shown. You'll also need to add a `using static` -statement at the top of the file so that you can reference the `Min` and `Max` methods without the +Create a new file; it can be any name ending with .cs. For example TelePrompterConfig.cs. Paste in the TelePrompterConfig class code, save and close. Put that class in the +`TeleprompterConsole` namespace as shown. Note the `using static` +statement allows you to reference the `Min` and `Max` methods without the enclosing class or namespace names. A [`using static`](../language-reference/keywords/using-directive.md) statement imports the methods from one class. This is in contrast with the `using` statement without `static`, which imports all classes from a namespace. -```csharp -using static System.Math; -``` - -Next, you need to update the `ShowTeleprompter` and `GetInput` methods to use the new `config` object. Write one final `Task` returning `async` method to start both tasks and exit when the first task finishes: +Next, you need to update the `ShowTeleprompter` and `GetInput` methods to use the new `config` object. To finish this feature, you need to create a new `async Task` returning method that starts both of these tasks (`GetInput` and `ShowTeleprompter`), and also manages the shared data between these two tasks. Create a RunTelePrompter task to start both tasks and exit when the first task finishes: ```csharp private static async Task RunTeleprompter() @@ -250,7 +261,7 @@ private static async Task RunTeleprompter() The one new method here is the call. That creates a `Task` that finishes as soon as any of the tasks in its argument list completes. -Next, you need to update both the `ShowTeleprompter` and `GetInput` methods to use the `config` object for the delay: +Next, you need to update both the `ShowTeleprompter` and `GetInput` methods to use the `config` object for the delay. The config object is being passed as a parameter to these methods. Use copy/paste to completely replace the methods with the new code here. You can see the code is using attributes and calling methods from the config object: ```csharp private static async Task ShowTeleprompter(TelePrompterConfig config) @@ -285,7 +296,7 @@ private static async Task GetInput(TelePrompterConfig config) } ``` -This new version of `ShowTeleprompter` calls a new method in the `TeleprompterConfig` class. Now, you need to update `Main` to call `RunTeleprompter` instead of `ShowTeleprompter`: +Now, you need to update `Main` to call `RunTeleprompter` instead of `ShowTeleprompter`: ```csharp await RunTeleprompter();