17. LINQ, Regex, Memory Management, Garbage Collector

LINQ to SQL

LINQ (Language Integrated Query) provides a unified query syntax to retrieve and manipulate data from various sources, including collections, XML documents, and SQL databases.

Basic Example

using (var context = new DataContext("your_connection_string"))
{
    Table<Customer> customers = context.GetTable<Customer>();

    var query = from c in customers
                where c.City == "London"
                select c;

    foreach (var customer in query)
    {
        Console.WriteLine(customer.Name);
    }
}

Regular Expressions (Regex)

Regular expressions are patterns used to match or validate strings, such as checking an email or phone number format. In C#, you can use the System.Text.RegularExpressions.Regex class to define and apply these patterns.

using System.Text.RegularExpressions;

bool IsValidEmail(string email)
{
    string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
    return Regex.IsMatch(email, pattern);
}

Memory Management in C#

Managed vs. Unmanaged Heap

  1. Managed Heap
    • Managed by the CLI (Common Language Infrastructure) or .NET runtime.
    • When you create an object (new SomeClass()), memory is allocated on the managed heap.
    • The .NET Garbage Collector (GC) eventually frees unused objects automatically.
    • Uses a Next Object Pointer to keep track of the next free address, which allows fast allocations.
  1. Unmanaged Heap
    • Managed manually with pointers.
    • Typically used with unsafe code blocks, or for interop with native libraries.
    • Not automatically garbage-collected.

Generations and the Garbage Collector

.NET GC employs a generational approach. Objects are categorized into Generations (0, 1, 2) depending on their lifespan:

When Gen 0 becomes full, the GC runs to reclaim memory. Short-lived objects are freed, and survivors move to Gen 1. Similarly, long-lived objects can move to Gen 2. This approach is efficient because most objects die young, so collecting Gen 0 frequently is relatively cheap.

GC Process

  1. Allocation: Creating new objects places them in Gen 0.
  1. Threshold Reached: When Gen 0 is full, GC triggers a collection.
  1. Mark and Compact: GC marks reachable objects and compacts memory by moving surviving objects together (defragmentation).
  1. Promotion: Surviving Gen 0 objects move to Gen 1; surviving Gen 1 objects move to Gen 2.
  1. Gen 2 collections happen less frequently, as these objects are assumed to be long-lived.

Finalizers vs. IDisposable

  1. Finalizer (~ClassName())
    • Called by the GC before reclaiming the object’s memory.
    • Expensive because the GC has to run twice on objects with finalizers (once to identify them, another to finalize).
    • Should be avoided if possible.
  1. IDisposable / Dispose
    • A more efficient pattern for releasing resources (e.g., file handles, database connections) deterministically.
    • Typically combined with a using statement for automatic disposal.
public class ResourceHolder : IDisposable
{
    // Acquire resources here

    public void Dispose()
    {
        // Release unmanaged resources here
        GC.SuppressFinalize(this); // Prevent finalizer call
    }
}

Using IDisposable and using is often preferred over relying on finalizers:

using (var holder = new ResourceHolder())
{
    // Work with resource
}  // Dispose is automatically called here

References

The content in this document is based on the original notes provided in Azerbaijani. For further details, you can refer to the original document using the following link:

Original Note - Azerbaijani Version