Let’s explore various aspects of C# programming through different code examples.

1. Parallel Processing with Task Parallel Library (TPL)

The first example demonstrates parallel processing using TPL:

ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 10 };
Parallel.For(0, 10, options, i =>
{
    // Parallel processing code here
});

// Resource-intensive computation
static void ResourceIntensiveFunction()
{
    double result = 0;
    for (int i = 0; i < 1000000000; i++)
    {
        result += Math.Sqrt(i);
    }
}

This code shows how to limit parallel processing to 10 threads and measure performance using Stopwatch.

2. Delegates in C#

Here’s an example of using delegates for method invocation:

public delegate int Operation(int a, int b);

public static int Add(int a, int b) => a + b;
public static int Multiply(int a, int b) => a * b;

public static void PerformOperation(int a, int b, Operation operation)
{
    int result = operation(a, b);
    Console.WriteLine("Result: " + result);
}

Delegates provide a way to pass methods as parameters, enabling flexible and reusable code.

3. Unicode Output in Console

Console applications can display Unicode characters:

Console.OutputEncoding = System.Text.Encoding.Unicode;
Console.WriteLine("\u2304");        // ⌄
Console.WriteLine("\U0001F4AF");    // 💯
Console.WriteLine("\U0001F604");    // 😄

Setting proper encoding ensures correct display of Unicode characters.

4. Asynchronous Matrix Operations

This example shows parallel matrix computations:

static async Task Main()
{
    const int N = 10000;
    Task[] tasks = new Task[4];
    for (int i = 0; i < 4; i++)
    {
        tasks[i] = Task.Run(() => ComputeMatrixInverse(N));
    }
    await Task.WhenAll(tasks);
}

The code demonstrates parallel matrix operations using Tasks for improved performance.

5. Task and Thread Management

Here’s an example of task and thread management:

Task singleTask = Task.Run(() => {
    Console.WriteLine($"Single task on thread {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(2000);
});

Parallel.For(0, 4, i => {
    Console.WriteLine($"Parallel {i} on thread {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(2000);
});

This demonstrates different ways to handle concurrent operations in C#.

6. Timer Implementation

Finally, a timer example:

private static System.Timers.Timer aTimer;

private static void SetTimer()
{
    aTimer = new System.Timers.Timer(2000);
    aTimer.Elapsed += OnTimedEvent;
    aTimer.AutoReset = true;
    aTimer.Enabled = true;
}

This shows how to implement recurring events using Timer in C#.

Each of these examples demonstrates different aspects of C# programming, from basic concepts to advanced parallel processing techniques. The code samples provide practical implementations that can be used as building blocks for larger applications.

volatile Keyword

Why volatile is Useful

In multi-threaded applications, threads may cache variables in their local memory, leading to inconsistencies when the variable is updated by another thread. The volatile keyword prevents such caching, ensuring that the variable is always read from the main memory, thus maintaining data consistency across threads.

How volatile is Used

To use the volatile keyword, simply declare the field with the volatile modifier. For example:

private volatile bool keepRunning = true;

In the provided code, keepRunning is marked as volatile to ensure that changes made by one thread (e.g., the Stop method) are immediately visible to other threads (e.g., the Worker method).

When to Use volatile

  • When a field is accessed by multiple threads without using locks.
  • When you need to ensure that the most recent value of a field is always read by all threads.

When Not to Use volatile

  • When more complex synchronization is required (e.g., atomic operations, multiple fields).
  • When you need to perform compound operations (e.g., incrementing a counter) that must be atomic.
  • When the field is not accessed by multiple threads.

In such cases, other synchronization mechanisms like lock, Monitor, or Interlocked should be used to ensure thread safety.


using System;
using System.Threading;

public class VolatileExample
{
    private volatile bool keepRunning = true;

    public void Start()
    {
        Thread workerThread = new Thread(Worker);
        workerThread.Start();
    }

    private void Worker()
    {
        while (keepRunning)
        {
            Console.WriteLine("Working...");
            Thread.Sleep(1000); // Simulate work
        }
        Console.WriteLine("Stopped working.");
    }

    public void Stop()
    {
        keepRunning = false;
    }

    public static void Main()
    {
        VolatileExample example = new VolatileExample();
        example.Start();

        // Simulate some work, then stop the worker thread
        Thread.Sleep(5000); // Let it run for 5 seconds
        example.Stop();
    }
}

Time Measurement in C#

Time is a critical aspect of many applications. C# provides several ways to measure, track, and represent time. Let’s explore the most common approaches:

DateTime

DateTime represents a specific point in time, typically expressed as a date and time of day.

// Get current date and time
DateTime now = DateTime.Now; // Local time
DateTime utcNow = DateTime.UtcNow; // UTC time

// Creating specific dates
DateTime specificDate = new DateTime(2023, 12, 31, 23, 59, 59);

// Parsing from string
DateTime parsedDate = DateTime.Parse("2023-01-01 12:30:45");

// Formatting
string formatted = now.ToString("yyyy-MM-dd HH:mm:ss");

// Date calculations
DateTime tomorrow = now.AddDays(1);
DateTime nextMonth = now.AddMonths(1);
TimeSpan difference = tomorrow - now;

TimeSpan

TimeSpan represents a time interval (duration).

// Create from explicit values
TimeSpan duration = new TimeSpan(2, 30, 45); // 2 hours, 30 minutes, 45 seconds
TimeSpan twoHours = TimeSpan.FromHours(2);
TimeSpan tenMinutes = TimeSpan.FromMinutes(10);

// Create from difference between DateTimes
DateTime start = DateTime.Now;
DateTime end = start.AddHours(3);
TimeSpan elapsed = end - start;

// Properties
double totalHours = elapsed.TotalHours;
int minutes = elapsed.Minutes; // Just the minutes component (0-59)

Stopwatch

Stopwatch provides high-precision timing for measuring code execution performance.

using System.Diagnostics;

// Basic usage
Stopwatch sw = new Stopwatch();
sw.Start();
// Code to measure
DoSomethingExpensive();
sw.Stop();

Console.WriteLine($"Execution took {sw.ElapsedMilliseconds} ms");
Console.WriteLine($"Or more precisely: {sw.Elapsed.TotalSeconds} seconds");

// Reusing stopwatch
sw.Reset(); // Resets back to zero
sw.Restart(); // Combines Reset() and Start()

Environment.TickCount

TickCount provides the number of milliseconds since the system started.

// Get system uptime in milliseconds (wraps around after ~25 days)
int tickCount = Environment.TickCount;

// Get system uptime in milliseconds (Int64 version, doesn't wrap)
long tickCount64 = Environment.TickCount64;

// Calculate elapsed time between operations
int start = Environment.TickCount;
DoSomeWork();
int end = Environment.TickCount;
int elapsed = end - start;

DateTimeOffset

DateTimeOffset represents a point in time with timezone awareness.

// Create with timezone information
DateTimeOffset now = DateTimeOffset.Now;
DateTimeOffset utcNow = DateTimeOffset.UtcNow;

// Create with specific offset
DateTimeOffset custom = new DateTimeOffset(2023, 12, 31, 23, 59, 59, TimeSpan.FromHours(-5));

// Get from DateTime
DateTimeOffset fromDateTime = new DateTimeOffset(DateTime.Now);

// Access timezone information
TimeSpan offset = now.Offset;

ValueStopwatch (High-Performance Option)

For extremely performance-sensitive code, a struct-based stopwatch can reduce allocations:

using System.Runtime.CompilerServices;

// Lightweight stopwatch (struct-based to avoid allocations)
public readonly struct ValueStopwatch
{
    private readonly long _startTimestamp;
    
    public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
    
    private ValueStopwatch(long startTimestamp)
    {
        _startTimestamp = startTimestamp;
    }
    
    public TimeSpan GetElapsedTime()
    {
        long end = Stopwatch.GetTimestamp();
        long elapsed = end - _startTimestamp;
        return TimeSpan.FromTicks(elapsed * TimeSpan.TicksPerSecond / Stopwatch.Frequency);
    }
}

// Usage
var watch = ValueStopwatch.StartNew();
DoSomeWork();
TimeSpan elapsed = watch.GetElapsedTime();

Choosing the Right Tool

  • DateTime: For calendar dates and times
  • DateTimeOffset: When timezone information matters
  • TimeSpan: For time intervals and calculations
  • Stopwatch: For precise code performance measurement
  • Environment.TickCount: For system uptime or simple elapsed time

Each approach has its strengths and is designed for specific scenarios. For performance-critical applications, Stopwatch is usually the best choice for timing code execution.

Observer Pattern in C#

The Observer pattern is a behavioral design pattern where an object (the subject) maintains a list of dependents (observers) and notifies them automatically of state changes. This pattern is fundamental for implementing distributed event handling systems.

Implementation Using Event Handlers

The most common way to implement the Observer pattern in C# is using events and delegates:

public class StockMarket
{
    // Event declaration using built-in EventHandler
    public event EventHandler<StockChangeEventArgs> StockPriceChanged;

    private decimal currentPrice;
    public decimal CurrentPrice
    {
        get { return currentPrice; }
        set
        {
            if (currentPrice != value)
            {
                currentPrice = value;
                // Notify observers about the price change
                OnStockPriceChanged(new StockChangeEventArgs(currentPrice));
            }
        }
    }

    // Method to raise the event
    protected virtual void OnStockPriceChanged(StockChangeEventArgs e)
    {
        StockPriceChanged?.Invoke(this, e);
    }
}

// Custom event args
public class StockChangeEventArgs : EventArgs
{
    public decimal NewPrice { get; }

    public StockChangeEventArgs(decimal newPrice)
    {
        NewPrice = newPrice;
    }
}

// Observer
public class StockTrader
{
    public string Name { get; }

    public StockTrader(string name, StockMarket market)
    {
        Name = name;
        // Subscribe to the event
        market.StockPriceChanged += OnStockPriceChanged;
    }

    private void OnStockPriceChanged(object sender, StockChangeEventArgs e)
    {
        Console.WriteLine($"{Name} noticed stock price changed to {e.NewPrice:C}");
    }
}

// Usage
public static void Main()
{
    var market = new StockMarket();
    var trader1 = new StockTrader("Alice", market);
    var trader2 = new StockTrader("Bob", market);

    market.CurrentPrice = 100.00m; // Both traders will be notified
    market.CurrentPrice = 105.75m; // Both traders will be notified again
}

This implementation is clean and straightforward, leveraging C#’s built-in event system. The StockMarket acts as the subject, while StockTrader instances are observers.

Implementation Using Reactive Extensions (Rx)

For more complex scenarios, Reactive Extensions provide a powerful way to implement the Observer pattern:

using System;
using System.Reactive.Subjects;

public class StockMarketRx
{
    // Subject acts as both an observable and an observer
    private readonly Subject<decimal> priceSubject = new Subject<decimal>();
    
    // Expose the observable interface to prevent direct calls to OnNext
    public IObservable<decimal> PriceChanges => priceSubject;

    private decimal currentPrice;
    public decimal CurrentPrice
    {
        get { return currentPrice; }
        set
        {
            if (currentPrice != value)
            {
                currentPrice = value;
                // Publish new price to all subscribers
                priceSubject.OnNext(currentPrice);
            }
        }
    }
}

public class StockTraderRx : IObserver<decimal>
{
    public string Name { get; }
    private IDisposable subscription;

    public StockTraderRx(string name, StockMarketRx market)
    {
        Name = name;
        // Subscribe to price changes
        subscription = market.PriceChanges.Subscribe(this);
    }

    public void OnCompleted()
    {
        Console.WriteLine($"{Name}: Market closed for the day");
    }

    public void OnError(Exception error)
    {
        Console.WriteLine($"{Name}: Error occurred: {error.Message}");
    }

    public void OnNext(decimal price)
    {
        Console.WriteLine($"{Name} noticed stock price changed to {price:C}");
    }

    public void Unsubscribe()
    {
        subscription?.Dispose();
    }
}

// Usage
public static void RxExample()
{
    var market = new StockMarketRx();
    var trader1 = new StockTraderRx("Alice", market);
    var trader2 = new StockTraderRx("Bob", market);

    market.CurrentPrice = 100.00m;
    market.CurrentPrice = 105.75m;
    
    // Unsubscribe one trader
    trader1.Unsubscribe();
    
    // Only trader2 gets this update
    market.CurrentPrice = 110.50m;
}

The Rx implementation provides additional benefits:

  • Built-in support for error handling
  • Explicit completion notification
  • Easy subscription management with IDisposable
  • Ability to transform and filter events using LINQ operators

Which Approach to Choose?

Use C# Events when:

  • You need a simple, lightweight implementation
  • Your application has straightforward publisher-subscriber relationships
  • You want to leverage familiar C# language features

Use Reactive Extensions when:

  • You need advanced event processing capabilities (filtering, throttling, etc.)
  • Your application deals with complex event streams
  • You want better error handling and resource cleanup
  • You need to compose multiple event sources together

Both approaches effectively implement the Observer pattern, but Reactive Extensions scales better for complex scenarios while adding a small learning curve and dependency.