Mon , Oct 12 2026
A delegate is a type-safe function pointer. It is a variable that holds a method instead of a plain value. Because it acts like a standard variable, you can pass a method as a parameter into other functions, store it, or swap it dynamically at runtime.
Think of a delegate like a universal remote control cable plug. You can plug it into an email sender, a text messaging sender, or a printer. The main control engine doesn't care what happens inside the machine; it just triggers whatever machine is currently plugged into the wire.
public delegate void NotificationHandler(string message);
public class OrderProcessor
{
public void Process(int orderId, NotificationHandler notify)
{
// Core business logic goes here...
// Fire whatever method is plugged into the delegate
notify?.Invoke($"Order {orderId} processed successfully.");
}
}
// Usage:
NotificationHandler h = NotificationServices.SendEmail;
h += NotificationServices.SendSMS; // Multicast: triggers both actions!
new OrderProcessor().Process(42, h);
Payment Gateways: When building a checkout system, passing a payment processor delegate allows you to plug in Stripe, PayPal, or Apple Pay dynamically without writing complex if/else statements inside your core checkout engine.
Instead of declaring custom public delegate signatures manually, .NET provides built-in shortcuts. These are ready-made generic delegates designed to handle almost any method signature shape.
| Type | Inputs | Returns | Best Used For |
|---|---|---|---|
| Action<...> | 0 to 16 | void (Nothing) |
Logging data, printing text, executing standalone tasks. |
| Func<..., TResult> | 0 to 16 | The last generic type specified | Math equations, data mappings, pulling database lookups. |
| Predicate<T> | Exactly 1 | bool (True/False) |
Validating inputs, filtering arrays, conditional checks. |
Action<string> log = s => Console.WriteLine(s);
Func<int, int, int> multiply = (x, y) => x * y; // Returns an int
Predicate<int> isAdult = age => age >= 18; // Returns true/false
Used heavily inside LINQ extensions. For example, .Where() accepts a Predicate to test rows, while .Select() takes a Func to alter and map objects from one model shape into another.
A ValueTuple is a lightweight, temporary value type container. It groups data elements together on the fly without forcing you to write a separate class file. It is best used for sending multiple values out of private local routines.
(bool success, string message) ValidateLogin(string username, string password)
{
return username == "admin" ? (true, "Success") : (false, "Denied");
}
// Usage (Deconstruction)
var (isOk, statusMsg) = ValidateLogin("admin", "pass");
Use Tuples for internal, short-lived operations. Use Classes or Records for your public API contracts, database objects, and structures that require long-term lifespan or specific behavior logic.
var handles type checking at compile time. The compiler looks at the expression, locks in the true type, and keeps complete IntelliSense support. dynamic shifts type checking entirely to runtime. The compiler stays quiet, but typos can cause immediate application crashes during active execution.
var age = 30; // Compiled permanently as an integer.
// age = "thirty"; // Compile error!
dynamic variableData = 30;
variableData = "thirty"; // Perfectly fine at runtime!
// variableData.InvalidMethod(); // Compiles fine, but crashes your app when executed.
Use var for 99% of regular development. Use dynamic explicitly when dealing with raw unmapped JSON strings, scripting integrations, or interacting with legacy Microsoft Office COM models.
Both keywords create unchangeable fields, but they evaluate at different application stages. const is hardcoded at compile time, meaning it can never look up dynamic values like system times. readonly resolves at runtime, allowing its value to be set inside a class constructor code block.
public class Settings
{
public const int MaxRetries = 3; // Fixed forever at compilation
public readonly DateTime BootTime;
public Settings() {
BootTime = DateTime.Now; // Allowed because it resolves at runtime
}
}
Use readonly to protect class fields that are populated via Dependency Injection (like logging engines or database contexts) so they cannot be overwritten mid-operation.
Primitives (like int, bool, double) hold their true data values directly inside their own memory location (the stack when local). Non-primitives (like class, array, interface) are reference types. The variable simply holds a memory address pointer on the stack, while the true complex data payload rests on the managed heap.
The Stack is an ultra-fast, structured memory system operating on a Last-In, First-Out (LIFO) plan. It manages execution tracking and value types, automatically wiping clean the second a method ends. The Heap is a large, shared playground for complex reference objects. It stays active until scanned and handled by the automated Garbage Collector.
Think of the stack as your immediate desktop surface where you put active worksheets. The heap is the large storage room downstairs. You can house huge boxes down there, but you need an index tracking card (a pointer) on your desktop to find them.
Boxing takes a raw value type and wraps it inside an object container on the heap. Unboxing casts it back out to the stack. This pattern causes major memory friction because every boxed instance demands a new heap memory allocation.
int balance = 500;
object box = balance; // Boxing happens here (Heap allocation)
int original = (int)box; // Unboxing happens here
// ❌ Avoid legacy styles that box values:
var oldCollection = new System.Collections.ArrayList();
oldCollection.Add(100);
// Use modern generic tools instead:
var clearCollection = new List<int>();
clearCollection.Add(100); // Fast, zero boxing!
The Garbage Collector automatic sweep goes through three primary stages: Mark (checks application root pointers to identify active rows), Sweep (removes unreferenced items), and Compact (shuffles live blocks together to eliminate gaps).
| Generation | Target Scope | Collection Cost |
|---|---|---|
| Gen 0 | Brand new, temporary local objects. | Lightning-fast, happens constantly. |
| Gen 1 | Buffer space holding Gen 0 survivors. | Short and occasional. |
| Gen 2 | Long-term resources, configurations, and singletons. | Heavy, slow Full GC impact. |
Note: Large Items exceeding 85,000 bytes go straight to a dedicated Large Object Heap (LOH) which skips standard compaction cycles to save processing cycles.
Yes. If a short-lived component hooks up to an event exposed by a long-term running infrastructure service but fails to unhook itself, the long-term root service retains that active memory address reference pointer, blocking the GC from reclaiming the memory space.
public class ViewTab : IDisposable
{
private readonly GlobalEventBroker _broker;
public ViewTab(GlobalEventBroker broker) {
_broker = broker;
_broker.RefreshRequested += OnRefresh; // Link established
}
public void Dispose() {
_broker.RefreshRequested -= OnRefresh; // Snapped! Prevents a leak.
}
private void OnRefresh(object s, EventArgs e) {}
}
IDisposable offers explicit, immediate cleanup for external unmanaged items (such as databases or network connections) using a using block structure. A Finalizer is an unguided fallback safety net called by the GC at an unexpected future point if a developer forgets manual tracking.
public class ResourceLink : IDisposable
{
private bool _disposed;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this); // Skips unnecessary finalizer queue tasks
}
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) { /* Clear managed objects */ }
/* Clear unmanaged native pointers here */
_disposed = true;
}
}
~ResourceLink() => Dispose(false); // Safety net rule
}
A Left Outer Join fetches every row from the primary source, alongside matching elements from the child source. If the child source lacks matching records, it populates a null fallback placeholder so parent rows aren't stripped out.
var leftJoin = from c in customers
join o in orders on c.Id equals o.CustomerId into groupData
from subOrder in groupData.DefaultIfEmpty()
select new { c.Name, Total = subOrder?.Total ?? 0 };
IEnumerable targets local memory tasks. Against databases, it downloads the whole target table over the network, running filters inside application memory. IQueryable constructs expression trees that translate into raw SQL scripts executed directly on the database engine server.
// ❌ BAD: Pulls entire table into memory first
var badSearch = db.Logs.AsEnumerable().Where(l => l.Level == "Error").Take(5);
// GOOD: Database processes request, returns 5 rows
var goodSearch = db.Logs.Where(l => l.Level == "Error").Take(5).ToList();
A LEFT JOIN shows all left table rows plus right table matching records. By introducing a WHERE right.Key IS NULL clause, you filter out rows that have matches, isolating orphan data records.
SELECT c.Id, c.Name
FROM Customers c
LEFT JOIN Orders o ON o.CustomerId = c.Id
WHERE o.Id IS NULL; -- Catches customers who have zero orders
System Cleanup: Isolating abandoned shopping carts or removing user accounts that haven't generated any transaction activity inside your logs.
A Non-Clustered Index is a separate ledger pointing back to raw data coordinates. If a query requests fields outside this index scope, the engine must make an extra trip to pull those data pieces, causing a slow Key Lookup. A Covering Index completely eliminates this extra lookup by attaching extra columns directly to the index using an INCLUDE clause.
CREATE NONCLUSTERED INDEX IX_Orders_Cust
ON Orders (CustomerId)
INCLUDE (OrderDate, Total); -- Covers queries completely, skipping extra lookup loops.
You can leverage a Common Table Expression (CTE) and a ROW_NUMBER() partition to group identical rows together, assigning sequential numbers to them, and then deleting any entries numbered greater than 1.
WITH CTE_Clean AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Email, Name ORDER BY (SELECT NULL)) AS RowNum
FROM Users
)
DELETE FROM CTE_Clean WHERE RowNum > 1;
These ranking formulas change how duplicate ties are prioritized inside data row sequences.
| Function | Handling of Ties | Sequence Outcome Example |
|---|---|---|
| ROW_NUMBER | Ties are ignored, gives direct sequence. | 1, 2, 3, 4, 5 |
| RANK | Shares same rank number, skips subsequent numbers. | 1, 2, 2, 2, 5 |
| DENSE_RANK | Shares same rank number, without skipping numbers. | 1, 2, 2, 2, 3 |
Both execute a subquery or table function for each row in the parent table. CROSS APPLY matches INNER JOIN behavior by removing parent rows that yield no subquery results. OUTER APPLY mimics a LEFT JOIN, retaining parent entries with NULL values if the subquery returns empty.
SELECT c.Name, x.Total
FROM Customers c
OUTER APPLY (SELECT TOP 1 Total FROM Orders o WHERE o.CustomerId = c.Id ORDER BY Total DESC) x;
Top N Reports: Generating a list of all clients alongside their single highest transaction value item.
WHERE filters raw data records before grouping tasks execute. HAVING filters summary groups after GROUP BY aggregations are processed.
SELECT CustomerId, SUM(Total)
FROM Orders
WHERE OrderDate >= '2026-01-01' -- Pre-filter raw entries
GROUP BY CustomerId
HAVING SUM(Total) > 1000; -- Post-filter aggregated groups
An Abstract Class defines core identity characteristics (an "is-a" relationship), allowing you to share fields, state variables, and constructor logic across child objects (limited to single inheritance). An Interface defines peripheral capability contracts (a "can-do" relationship), establishing method blueprints without containing instance state variables (supporting multiple implementations).
public abstract class Vehicle {
public string Model { get; }
protected Vehicle(string model) => Model = model;
public abstract void Move();
}
public interface IFlyable { void TakeOff(); }
public class Airplane : Vehicle, IFlyable {
public Airplane(string m) : base(m) {}
public override void Move() => Console.WriteLine("Rolling down runway");
public void TakeOff() => Console.WriteLine("Ascending into sky");
}
new keyword.Ensures that exactly one instance of a class is ever constructed across the application runtime, utilizing double-check locking to eliminate threading conflicts without introducing performance bottlenecks.
public sealed class SystemCache
{
private static volatile SystemCache _instance;
private static readonly object _lock = new();
private SystemCache() {} // Blocks external creation
public static SystemCache Instance {
get {
if (_instance == null) { // First check: Skips locking if already built
lock (_lock) {
if (_instance == null) { // Second check: Safely creates instance
_instance = new SystemCache();
}
}
}
return _instance;
}
}
}
Factory Method uses a single creation method to return a single object instance based on subclass decisions. Abstract Factory orchestrates a suite of multiple factories to build out a cohesive family of related, unlinked components (like generating Mac menus, Mac input buttons, and Mac scrollbars together).
To maximize performance in Cosmos DB, you must avoid exhausting system ports, optimize query network routing, and manage your Request Unit (RU) costs efficiently.
CosmosClient object as a permanent Singleton instance to protect your application from socket exhaustion.ConnectionMode.Direct settings to bypass gateway proxies and connect straight to partition backends via raw TCP.// Safe setup routine
builder.Services.AddSingleton(new CosmosClient(connectionString, new CosmosClientOptions {
ConnectionMode = ConnectionMode.Direct
}));
// Most cost-effective point read retrieval style
var record = await container.ReadItemAsync<User>(id, new PartitionKey(userCountry));
Queues operate on a one-to-one delivery plan; exactly one consumer processes each message. Topics provide a one-to-many publish-and-subscribe pipeline, broadcasting copies of a single message out to multiple subscribers simultaneously.
Disable the AutoCompleteMessages parameter. Explicitly acknowledge and call CompleteMessageAsync **only after** your database updates have been committed to disk. If your application crashes mid-process, the unacknowledged message safely returns to the queue instead of being lost forever.
var processingOptions = new ServiceBusProcessorOptions { AutoCompleteMessages = false };
processor.ProcessMessageAsync += async args => {
await ApplyChangesToSqlDatabase(args.Message); // Step 1: Save data first
await args.CompleteMessageAsync(args.Message); // Step 2: Clear from queue last
};
This design splits your custom function logic out into a distinct console execution process entirely separate from the underlying Azure hosting system process. This isolation gives you full control over application dependencies and allows you to adopt new versions of .NET immediately.
Crucial Rule: Keep functions completely stateless so the automated consumption hosting plans can scale out to many instances during traffic spikes without creating data sync conflicts.
var appHost = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services => services.AddTransient<IEmailService, EmailService>())
.Build();
appHost.Run();
I am an engineer with over 10 years of experience, passionate about using my knowledge and skills to make a difference in the world. By writing on LifeDB, I aim to share my thoughts, ideas, and insights to inspire positive change and contribute to society. I believe that even small ideas can create big impacts, and I am committed to giving back to the community in every way I can.
Leave a Reply