using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
// Example 1: Combining collections using Concat (simulate spread operator)
class Example1
{
public static void Run()
{
List<int> listA = new List<int> { 1, 2 };
List<int> listB = new List<int> { 3, 4 };
var combinedList = listA.Concat(listB).ToList();
Console.WriteLine("Combined List using Concat (simulate spread operator): " + string.Join(", ", combinedList));
// Output: 1, 2, 3, 4
}
}
// Example 2: Updating a list using .Select with a return statement in a lambda block.
class Example2
{
public static void Run()
{
List<int> numbers = new List<int> { 10, 20, 30 };
var mappedList = numbers.Select(num =>
// var mappedList = numbers.Select((num, index) =>
{
// Using block syntax with an explicit return statement to create an anonymous object.
return new { Original = num, Double = num * 2 };
}).ToList();
Console.WriteLine("\nMapped List using Select with a return key:");
foreach (var item in mappedList)
{
Console.WriteLine($"Original: {item.Original}, Double: {item.Double}");
}
// Output: Anonymous objects printed for each number.
}
}
// Example 3: Chained syntax "Where().All().ToList()"
class Example3
{
public static void Run()
{
// Wrap the list into our chainable type to allow a chainable All.
List<int> numberList = new List<int> { 2, 4, 6, 8 };
// Note: The original example's `.ToList().All()` would result in a boolean for `filteredList`.
// If you intended to keep the list and then check 'All' as a separate step,
// you would assign the ToList() result to a variable first.
// For demonstration purposes, I'm showing the boolean result as implied by original chain.
bool allEven = numberList
.Where(n => n > 0) // Filter: keep numbers > 0
.All(n => n % 2 == 0); // Check: if all numbers are even
Console.WriteLine("\nResult of Where().All() chain: " + allEven);
// Output: Result of Where().All() chain: True
}
}
// Example 4: Using a lambda with fallback logic
class Example4
{
public static void Run(object mappedList)
{
string result = new Func<string>(() =>
{
try
{
// This line was commented out in the original, but uncommented for demonstration
// throw new Exception();
// Assuming mappedList is accessible and not null, otherwise "Alternate value" will be used.
// In a real scenario, you'd pass mappedList or define it within this scope.
return (mappedList as List<object>)?.FirstOrDefault()?.ToString() ?? "Alternate value";
}
catch
{
return "Alternate value";
}
})();
Console.WriteLine($"\n{result}");
// Output: Either "{ Original = 10, Double = 20 }" or "Alternate value" based on the state and exception.
}
}
// Example 5: Manually Creating the Logger
public class MyClassService
{
private readonly ILogger<MyClassService> _logger;
public MyClassService(ILogger<MyClassService> logger)
{
_logger = logger;
_logger.LogInformation("MyClassService initialized.");
}
public void DoSomething()
{
_logger.LogInformation("Doing something important.");
}
}
class Example5
{
public static void Run()
{
var loggerFactory = LoggerFactory.Create(builder =>
{
// Configure logging providers
builder.AddConsole();
// Optionally add more providers here (e.g., Debug, EventSource)
});
ILogger<MyClassService> logger = loggerFactory.CreateLogger<MyClassService>();
// ILogger<MyClassService> logger = new LoggerFactory().CreateLogger<MyClassService>(); // This would create a new factory every time
// Now you can pass this logger to your service
var myClassService = new MyClassService(logger);
myClassService.DoSomething();
}
}
class Program
{
static void Main(string[] args)
{
try
{
Example1.Run();
Example2.Run();
// For Example 4, we need to pass a list (or simulate it)
// Let's create a dummy list for Example 4 based on Example 2's output structure
List<object> dummyMappedList = new List<object>
{
new { Original = 10, Double = 20 } // Simulating an item from mappedList
};
Example4.Run(dummyMappedList);
Example3.Run(); // Changed order to make Example 3's output clearer after previous runs
Example5.Run();
}
catch (Exception ex)
{
// 1. Catch the error
Console.Error.WriteLine($"[LOG] An error occurred in PerformOperationWithLogging: {ex.Message}");
// You might log more details, like ex.StackTrace, to a file or monitoring system here.
// 2. Re-throw the original exception
// IMPORTANT: Just 'throw;' preserves the original stack trace.
// 'throw ex;' (BAD) would reset the stack trace to this point, losing origin info.
throw;
}
Console.WriteLine("--- Demonstrating Alternatives and Why .Wait() Is Bad ---");
var demo = new AsyncAlternatives();
Console.WriteLine("\n--- 1. Fire and Forget (Ignoring the Task) ---");
// Good for truly background, non-critical tasks where exceptions are handled internally.
// Bad for: No completion notification, unobserved exceptions if not handled internally.
demo.FireAndForgetCall();
Console.WriteLine("Main thread continues immediately after FireAndForgetCall.");
Task.Delay(500).Wait(); // Give async task a moment to start
Console.WriteLine("--------------------------------------------\n");
Console.WriteLine("--- 2. Task.Run() (Offloading to Thread Pool) ---");
// Good for: Moving CPU-bound work off the main thread, fire-and-forget.
// Bad for: Same unobserved exception issues as pure fire-and-forget, slight overhead.
demo.TaskRunCall();
Console.WriteLine("Main thread continues immediately after TaskRunCall.");
Task.Delay(500).Wait(); // Give async task a moment to start
Console.WriteLine("----------------------------------------------\n");
Console.WriteLine("--- 3. ContinueWith (For Completion/Error Handling) ---");
// Good for: Observing completion or handling exceptions from a fire-and-forget task without blocking.
// Bad for: Can add complexity, less readable than async/await.
demo.ContinueWithCall();
Console.WriteLine("Main thread continues immediately after ContinueWithCall.");
Task.Delay(3000).Wait(); // Give async task and continuation a moment to complete
Console.WriteLine("-----------------------------------------------\n");
Console.WriteLine("--- Why .Wait() Is Bad (and .Result too) ---");
// .Wait() blocks the calling thread, leading to unresponsiveness and deadlocks.
// It also wraps exceptions in AggregateException, making error handling clunky.
try
{
Console.WriteLine("Attempting to use .Wait() - This will block the current thread.");
demo.DoSomethingAsync().Wait(); // BAD: Blocks the Main thread
Console.WriteLine("If this were a UI app, the UI would freeze here.");
Console.WriteLine("If this were ASP.NET, a request thread would be tied up.");
}
catch (AggregateException ex)
{
Console.WriteLine($"Caught an AggregateException from .Wait(): {ex.InnerExceptions[0].Message}");
// Better error handling: foreach (var inner in ex.InnerExceptions) { Console.WriteLine(inner.Message); }
}
// DEADLOCK POTENTIAL (common in UI/ASP.NET):
// If DoSomethingAsync internally used await on a context that requires the current thread,
// and that thread is blocked by .Wait(), it would deadlock.
Console.WriteLine("\n--- Conclusion ---");
Console.WriteLine("Prefer 'await' whenever possible to enable true asynchronous, non-blocking execution.");
Console.WriteLine("Only use 'async void' for event handlers, with internal exception handling.");
Console.WriteLine("For other 'void' methods, consider fire-and-forget with internal error handling,");
Console.WriteLine("or 'ContinueWith' for observing completion/errors if not awaiting is an absolute must.");
}
class AsyncAlternatives
{
// A simulated async task for demonstrations
public async Task DoSomethingAsync()
{
Console.WriteLine(" [Async] DoSomethingAsync started.");
await Task.Delay(1000); // Simulate network call or I/O
// Uncomment the line below to see exception handling in action for different scenarios
// throw new InvalidOperationException("Simulated error in async task!");
Console.WriteLine(" [Async] DoSomethingAsync finished.");
}
// 1. Fire and Forget
public void FireAndForgetCall()
{
Console.WriteLine(" Calling DoSomethingAsync() and discarding the Task.");
_ = DoSomethingAsync(); // Use '_' discard operator for clarity
}
// 2. Task.Run()
public void TaskRunCall()
{
Console.WriteLine(" Wrapping DoSomethingAsync() in Task.Run().");
_ = Task.Run(async () => await DoSomethingAsync());
}
// 3. ContinueWith
public void ContinueWithCall()
{
Console.WriteLine(" Calling DoSomethingAsync() and attaching a ContinueWith.");
DoSomethingAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
foreach (var ex in task.Exception.InnerExceptions)
{
Console.Error.WriteLine($" [ContinueWith Error] Async task failed: {ex.Message}");
}
}
else if (task.IsCompletedSuccessfully)
{
Console.WriteLine(" [ContinueWith Success] Async task completed successfully.");
}
// Add else if (task.IsCanceled) for cancellation handling
}, TaskScheduler.Default); // Ensures continuation runs on a thread pool thread
}
}
}