Master advanced C# features that every professional developer should know.
Delegates
Definition: Type-safe function pointers that reference methods with a specific signature.
Basic Delegate
// Define delegate type
public delegate void NotifyDelegate(string message);
// Method matching delegate signature
public void SendEmail(string message)
{
Console.WriteLine($"Email: {message}");
}
// Usage
NotifyDelegate notify = SendEmail;
notify("Hello World"); // Calls SendEmail
Multicast Delegates
NotifyDelegate notify = SendEmail;
notify += SendSMS;
notify += LogToFile;
notify("Alert!"); // Calls all three methods
Built-in Delegates
// Action<T> - no return value
Action<string> print = (msg) => Console.WriteLine(msg);
print("Hello");
// Func<T, TResult> - has return value
Func<int, int, int> add = (a, b) => a + b;
int sum = add(5, 3);
// Predicate<T> - returns bool
Predicate<int> isEven = (n) => n % 2 == 0;
bool result = isEven(4); // true
Lambda Expressions
Definition: Anonymous functions that provide a concise syntax for writing inline methods.
Lambda Syntax
// Expression lambda
Func<int, int> square = x => x * x;
// Statement lambda
Func<int, int, int> max = (a, b) =>
{
if (a > b) return a;
return b;
};
// Multiple parameters
Func<int, int, int> multiply = (x, y) => x * y;
Lambda with LINQ
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
// Filter even numbers
var evens = numbers.Where(n => n % 2 == 0);
// Transform
var squared = numbers.Select(n => n * n);
// Aggregate
var sum = numbers.Aggregate((a, b) => a + b);
LINQ (Language Integrated Query)
Definition: Provides query capabilities directly in C# for collections, databases, and XML.
Query Syntax
var students = new List<Student>
{
new Student { Name = "Alice", Grade = 85 },
new Student { Name = "Bob", Grade = 92 },
new Student { Name = "Charlie", Grade = 78 }
};
// Query syntax
var topStudents = from s in students
where s.Grade >= 80
orderby s.Grade descending
select s.Name;
Method Syntax
// Method syntax (same query)
var topStudents = students
.Where(s => s.Grade >= 80)
.OrderByDescending(s => s.Grade)
.Select(s => s.Name);
Common LINQ Operations
// Filtering
var adults = people.Where(p => p.Age >= 18);
// Projection
var names = people.Select(p => p.Name);
// Ordering
var sorted = people.OrderBy(p => p.Age);
// Grouping
var grouped = people.GroupBy(p => p.City);
// Aggregation
var average = numbers.Average();
var max = numbers.Max();
var count = people.Count(p => p.Age > 30);
// First/Single
var first = people.First();
var single = people.Single(p => p.Id == 1);
Entity Framework (EF)
Definition: Object-Relational Mapping (ORM) framework for .NET that enables developers to work with databases using .NET objects.
Code-First Approach
// Define entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// DbContext
public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer("connection_string");
}
}
CRUD Operations
using (var context = new AppDbContext())
{
// Create
var product = new Product { Name = "Laptop", Price = 999.99m };
context.Products.Add(product);
context.SaveChanges();
// Read
var products = context.Products.Where(p => p.Price > 500).ToList();
// Update
var item = context.Products.Find(1);
item.Price = 899.99m;
context.SaveChanges();
// Delete
context.Products.Remove(item);
context.SaveChanges();
}
Relationships
public class Order
{
public int Id { get; set; }
public DateTime OrderDate { get; set; }
// Navigation property
public List<OrderItem> Items { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
// Navigation properties
public Order Order { get; set; }
public Product Product { get; set; }
}
Async/Await
Definition: Asynchronous programming pattern that allows non-blocking operations.
Async Method
public async Task<string> DownloadDataAsync(string url)
{
using (var client = new HttpClient())
{
string result = await client.GetStringAsync(url);
return result;
}
}
// Usage
string data = await DownloadDataAsync("https://api.example.com");
Console.WriteLine(data);
Extension Methods
Definition: Add new methods to existing types without modifying them.
public static class StringExtensions
{
public static bool IsValidEmail(this string email)
{
return email.Contains("@") && email.Contains(".");
}
public static string Reverse(this string str)
{
return new string(str.Reverse().ToArray());
}
}
// Usage
string email = "test@example.com";
bool valid = email.IsValidEmail(); // true
string text = "Hello";
string reversed = text.Reverse(); // "olleH"
C# Operators Explained
Understanding modern C# operators is crucial for writing clean, expressive code. Here are the most important operators explained clearly for intermediate developers.
1. Lambda Operator (=>)
The => operator separates parameters from the expression or statement block in lambda
expressions.
// Lambda expression: parameter => expression
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // Output: 25
// Multiple parameters
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 4)); // Output: 7
// Statement lambda (with curly braces)
Action<string> greet = name =>
{
var message = $"Hello, {name}!";
Console.WriteLine(message);
};
// Used in LINQ
var evens = numbers.Where(n => n % 2 == 0);
2. Null-Coalescing Operator (??)
Returns the left operand if it's not null; otherwise, returns the right operand.
// Basic usage
string name = null;
string displayName = name ?? "Guest";
Console.WriteLine(displayName); // Output: Guest
// Chaining
string result = first ?? second ?? third ?? "Default";
// Common use case
var timeout = config.Timeout ?? 30;
3. Null-Coalescing Assignment (??=)
Assigns the right operand to the left operand only if the left operand is null.
// Before: verbose null check
if (cache == null)
cache = new Dictionary<string, object>();
// After: concise with ??=
cache ??= new Dictionary<string, object>();
// Lazy initialization
private Database _db;
public Database GetDatabase()
{
_db ??= new Database(); // Initialize only once
return _db;
}
4. Null-Conditional Operator (?. and ?[])
Safely access members and elements, returning null if the object is null instead of throwing NullReferenceException.
// Member access (?.)
Person person = null;
string name = person?.Name; // Returns null, doesn't throw
// Chaining
var length = person?.Name?.Length;
// Array/indexer access (?[])
int[] numbers = null;
int? first = numbers?[0]; // Returns null
// Real-world example
var city = customer?.Address?.City ?? "Unknown";
// Safe event invocation
OnDataChanged?.Invoke(this, EventArgs.Empty);
5. Range Operator (..)
Creates a range of indices for slicing arrays and collections.
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Get elements from index 2 to 5 (exclusive)
var slice1 = numbers[2..5]; // { 2, 3, 4 }
// From start to index 3
var slice2 = numbers[..3]; // { 0, 1, 2 }
// From index 7 to end
var slice3 = numbers[7..]; // { 7, 8, 9 }
// Last 3 elements
var slice4 = numbers[^3..]; // { 7, 8, 9 }
// String slicing
string text = "Hello World";
string hello = text[..5]; // "Hello"
6. Index from End Operator (^)
Specifies an index from the end of a sequence.
int[] numbers = { 1, 2, 3, 4, 5 };
var last = numbers[^1]; // 5 (last element)
var secondLast = numbers[^2]; // 4
// With strings
string text = "Programming";
char lastChar = text[^1]; // 'g'
// Combined with range
var lastThree = numbers[^3..]; // { 3, 4, 5 }
7. Namespace Alias Qualifier (::)
Accesses members of an aliased namespace, useful when there are naming conflicts.
// Define alias at top of file
using MyCompany = Company.MyApplication.Core;
using ThirdParty = ExternalLibrary.Core;
// Both have a class named "Logger"
MyCompany::Logger logger1 = new MyCompany::Logger();
ThirdParty::Logger logger2 = new ThirdParty::Logger();
// Global namespace alias
global::System.Console.WriteLine("Using global System");
8. Switch Expression (=>)
The => operator is also used in switch expressions for pattern matching.
// Modern switch expression
string GetDayType(DayOfWeek day) => day switch
{
DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
_ => "Weekday"
};
// With pattern matching
decimal CalculateDiscount(Customer customer) => customer switch
{
{ IsPremium: true, YearsActive: > 5 } => 0.20m,
{ IsPremium: true } => 0.15m,
{ YearsActive: > 3 } => 0.10m,
_ => 0.05m
};
9. Discard Operator (_)
Indicates that a value is intentionally unused.
// Ignore out parameters
if (int.TryParse("123", out _))
Console.WriteLine("Valid number");
// Deconstruction - ignore some values
var (name, _, age) = GetPersonInfo();
// Pattern matching - default case
var result = value switch
{
1 => "One",
2 => "Two",
_ => "Other"
};
10. Null-Forgiving Operator (!)
Tells the compiler that a nullable expression is not null (use with caution!).
string? nullableString = GetString();
// Without !, compiler warns
int length1 = nullableString.Length; // Warning!
// With !, suppress the warning
int length2 = nullableString!.Length; // No warning
// After null check
if (name != null)
ProcessName(name!); // Tell compiler it's safe
// WARNING: Only use when CERTAIN it's not null!
Operator Quick Reference
| Operator | Name | Purpose | Example |
|---|---|---|---|
=> |
Lambda | Define lambda expressions | x => x * 2 |
?? |
Null-coalescing | Provide default for null | name ?? "Guest" |
??= |
Null-coalescing assignment | Assign if null | list ??= new() |
?. |
Null-conditional | Safe member access | obj?.Property |
?[] |
Null-conditional index | Safe indexer access | arr?[0] |
.. |
Range | Create index range | arr[2..5] |
^ |
Index from end | Index from end of sequence | arr[^1] |
:: |
Namespace alias | Access aliased namespace | MyAlias::Class |
_ |
Discard | Ignore unused values | var (x, _) = tuple |
! |
Null-forgiving | Suppress null warning | obj!.Method() |
Summary
| Concept | Purpose | Use Case |
|---|---|---|
| Delegates | Function pointers | Callbacks, events |
| Lambda | Anonymous functions | LINQ, inline logic |
| LINQ | Query collections | Data filtering, transformation |
| EF | ORM framework | Database operations |
| Async/Await | Asynchronous programming | Non-blocking I/O |