1. Asynchronous Programming: async
and await
Asynchronous programming in C# is simplified by the async
and await
keywords. They allow you to write non-blocking code that performs long-running tasks like I/O operations (e.g., reading files, web requests) without blocking the main thread.
async
: Marks a method as asynchronous.await
: Waits for an asynchronous task to complete without blocking the thread.
Example: Simple Asynchronous Method with async
and await
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Starting async operation...");
// Call the asynchronous method
string result = await DoWorkAsync();
Console.WriteLine(result); // Output: Work completed!
Console.WriteLine("Async operation finished.");
}
static async Task<string> DoWorkAsync()
{
// Simulate an asynchronous operation
await Task.Delay(2000); // Wait for 2 seconds
return "Work completed!";
}
}
How It Works:
DoWorkAsync
is marked asasync
, meaning it contains asynchronous operations.await Task.Delay(2000)
simulates a delay (asynchronous operation). Theawait
keyword tells the compiler to wait for the operation to complete without blocking the main thread.- When
await
is encountered, control returns to the calling method, allowing other work to be done in the meantime.
2. Caller Information Attributes
Caller Information Attributes allow you to obtain information about the caller of a method, such as the file path, line number, or the member name. This is particularly useful for logging, diagnostics, and debugging.
Caller attributes include:
CallerMemberName
: Gets the name of the method or property that called the method.CallerFilePath
: Gets the full path of the source file where the method was called.CallerLineNumber
: Gets the line number in the source file where the method was called.
These attributes are specified in the method signature using optional parameters.
Example: Caller Information Attributes for Logging
using System;
using System.Runtime.CompilerServices;
class Program
{
static void Main()
{
Log("This is a log message.");
}
static void Log(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
Console.WriteLine($"Message: {message}");
Console.WriteLine($"Called by: {memberName}");
Console.WriteLine($"File path: {filePath}");
Console.WriteLine($"Line number: {lineNumber}");
}
}
How It Works:
- The
Log
method receives the caller's information automatically using theCallerMemberName
,CallerFilePath
, andCallerLineNumber
attributes. - When
Log
is called fromMain
, the output will display the method name (Main
), the file path, and the line number whereLog
was called.
3. Improved Exception Handling with Asynchronous Code
Handling exceptions in asynchronous methods follows specific rules. If an asynchronous method throws an exception, it is stored in the Task
object and re-thrown when awaited. You should wrap await
calls in try-catch
blocks to handle exceptions.
Example: Async Exception Handling
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
try
{
// Call an async method that throws an exception
await DoWorkAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Exception caught: {ex.Message}");
}
}
static async Task DoWorkAsync()
{
await Task.Delay(1000); // Simulate work
throw new InvalidOperationException("An error occurred during async operation.");
}
}
How It Works:
- If
DoWorkAsync
throws an exception, it is propagated through theTask
object. - The exception is caught in the
try-catch
block that surrounds theawait DoWorkAsync()
call.
Additional Notes on Asynchronous Exception Handling
Unobserved Exceptions: If you do not
await
a task and it throws an exception, the exception will be "unobserved" until the task is finalized by the garbage collector. This can lead to unexpected behavior, especially in long-running applications.Task Exception Propagation: If you are not using
await
but instead are manually handling the task (e.g., usingContinueWith
), you need to manually check for exceptions by accessing theTask.Exception
property.
Example: Handling Multiple Tasks with Task.WhenAll
When running multiple tasks concurrently, you can use Task.WhenAll
to wait for all tasks to complete, and handle exceptions across tasks.
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
try
{
Task task1 = Task.Run(() => throw new InvalidOperationException("Task 1 failed."));
Task task2 = Task.Run(() => throw new ArgumentException("Task 2 failed."));
// Wait for all tasks to complete
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
Console.WriteLine($"Exception caught: {ex.Message}");
}
}
}
How It Works:
Task.WhenAll
waits for all tasks to complete. If any task fails, it propagates the exception.- The exception is caught and handled in the
try-catch
block.
Summary of Key Points
- Asynchronous Programming with
async
andawait
allows you to write non-blocking code. Theawait
keyword suspends the execution of the async method until the awaited task completes. - Caller Information Attributes (
CallerMemberName
,CallerFilePath
,CallerLineNumber
) provide information about the method’s caller, aiding in logging and diagnostics. - Improved Exception Handling in asynchronous programming involves catching exceptions from
async
methods usingtry-catch
blocks aroundawait
. This allows you to handle errors in asynchronous operations effectively.
These concepts make C# a more powerful language for modern application development, enabling easier asynchronous programming, enhanced diagnostics, and robust error handling.