1. Null-Conditional Operator for Safe Access
The null-conditional operator ?.
simplifies null checking, reducing the need for verbose code.
string name = person?.Address?.City ?? "Unknown";
This checks if person
or Address
is null, and if so, returns "Unknown"
. Otherwise, it returns the value of City
.
2. String Interpolation
String interpolation is a cleaner way to format strings, introduced in C# 6. It’s concise and readable compared to string.Format
.
int age = 30;
string name = "John";
string greeting = $"Hello, my name is {name} and I'm {age} years old.";
3. LINQ to Filter and Sort Collections
LINQ (Language Integrated Query) is an essential tool for querying and transforming collections.
var numbers = new List<int> { 1, 4, 7, 3, 9, 6 };
var evenNumbers = numbers.Where(n => n % 2 == 0).OrderBy(n => n).ToList();
foreach (var num in evenNumbers)
{
Console.WriteLine(num); // Outputs: 4, 6
}
This filters for even numbers and sorts them in ascending order.
4. Exception Filters
Exception filters allow you to conditionally catch exceptions. This feature is useful for logging specific types of exceptions or rerouting control flow based on conditions.
try
{
// Some code that may throw an exception
}
catch (Exception ex) when (ex.Message.Contains("Specific Error"))
{
Console.WriteLine("Caught a specific exception");
}
5. Auto-Implemented Properties with Initializers
You can initialize properties directly within your class, simplifying object construction without the need for explicit constructors.
public class User
{
public string Name { get; set; } = "Unknown";
public int Age { get; set; } = 0;
}
6. Using using
for Efficient Resource Management
The using
statement ensures that resources are properly disposed of after use. This is commonly used for working with IDisposable
objects like database connections or file streams.
using (var file = new StreamWriter("file.txt"))
{
file.WriteLine("Hello, World!");
}
using
ensures that the StreamWriter
is closed and disposed of, preventing resource leaks.
7. Task.Delay in Async Methods
When writing asynchronous methods, Task.Delay
is a simple way to simulate a delay without blocking the thread.
public async Task SimulateWorkAsync()
{
await Task.Delay(1000); // Wait for 1 second
Console.WriteLine("Task completed");
}
This is useful for testing or simulating non-blocking wait times.
8. Parallel.For for Multithreaded Execution
Parallel.For
is a great way to execute loop iterations in parallel, making full use of multi-core processors.
Parallel.For(0, 10, i =>
{
Console.WriteLine($"Processing {i} on thread {Thread.CurrentThread.ManagedThreadId}");
});
This will print the loop iterations in parallel across multiple threads, improving performance for CPU-bound operations.
9. Regular Expressions for Pattern Matching
C# provides the Regex
class for powerful text matching and manipulation.
string pattern = @"\d+"; // Matches one or more digits
string input = "My phone number is 12345.";
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine($"Found number: {match.Value}"); // Outputs: Found number: 12345
}
Regular expressions can be used for complex string validation, parsing, and manipulation.
10. Weak Event Pattern
The weak event pattern helps avoid memory leaks in event handlers, which is especially useful in long-lived applications where object references can prevent garbage collection.
var timer = new Timer(1000);
WeakReference<EventHandler> weakHandler = new WeakReference<EventHandler>(null);
EventHandler handler = null;
handler = (s, e) =>
{
Console.WriteLine("Timer ticked");
if (!weakHandler.TryGetTarget(out _))
{
timer.Elapsed -= handler;
}
};
weakHandler.SetTarget(handler);
timer.Elapsed += handler;
timer.Start();
This snippet ensures that the event handler does not prevent the timer or the listener from being garbage collected, avoiding memory leaks.