8  Late Binding in C# and .NET

Late binding is about deferring decisions about which concrete implementation to use until runtime (or configuration time), rather than hardcoding those decisions at compile time. This gives you flexibility to swap implementations without rebuilding your application.

8.1 The Core Concept

Think of it like this:

COMPILE TIME (Early Binding)
┌──────────────────────────────────────┐
│  Application Code                    │
│  ↓                                   │
│  Hardcoded: new SqlServerRepository()│
│  ↓                                   │
│  SQL Server only                     │
└──────────────────────────────────────┘
Problem: Must recompile to change database!


RUNTIME (Late Binding)
┌─────────────────────────────────────┐
│  Application Code                   │
│  ↓                                  │
│  Uses: IRepository interface        │
│  ↓                                  │
│  Configuration decides at runtime:  │
│    - SqlServerRepository, OR        │
│    - OracleRepository               │
└─────────────────────────────────────┘
Benefit: Change via config file, no recompile!

8.2 C# Example: Database Abstraction

Let’s build the database example from your excerpt:

8.2.1 Step 1: Define the Interface

// IRepository.cs
public interface IRepository
{
    void SaveData(string data);
    string GetData(int id);
}

8.2.2 Step 2: Create Multiple Implementations

// SqlServerRepository.cs
public class SqlServerRepository : IRepository
{
    public void SaveData(string data)
    {
        Console.WriteLine($"Saving to SQL Server: {data}");
        // Actual SQL Server code here
    }

    public string GetData(int id)
    {
        return $"Data from SQL Server with ID: {id}";
    }
}

// OracleRepository.cs
public class OracleRepository : IRepository
{
    public void SaveData(string data)
    {
        Console.WriteLine($"Saving to Oracle: {data}");
        // Actual Oracle code here
    }

    public string GetData(int id)
    {
        return $"Data from Oracle with ID: {id}";
    }
}

8.2.3 Step 3: Application Code (Depends on Interface Only)

// DataService.cs
public class DataService
{
    private readonly IRepository _repository;

    // Constructor injection - doesn't know which implementation!
    public DataService(IRepository repository)
    {
        _repository = repository;
    }

    public void ProcessData()
    {
        _repository.SaveData("Patient scan data");
        var data = _repository.GetData(123);
        Console.WriteLine(data);
    }
}

8.2.4 Step 4: Late Binding via Configuration

Option A: Using appsettings.json

// appsettings.json
{
  "DatabaseProvider": "SqlServer"  // or "Oracle"
}

Option B: Binding at Application Startup

// Program.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var services = new ServiceCollection();

// Late binding: Decision made at runtime based on config
string provider = configuration["DatabaseProvider"];

if (provider == "SqlServer")
{
    services.AddScoped<IRepository, SqlServerRepository>();
}
else if (provider == "Oracle")
{
    services.AddScoped<IRepository, OracleRepository>();
}

services.AddScoped<DataService>();

var serviceProvider = services.BuildServiceProvider();

// Use the service
var dataService = serviceProvider.GetService<DataService>();
dataService.ProcessData();

8.3 Flow Diagram

┌──────────────────────────────────────────────────────┐
│  Compile Time                                        │
│  ────────────────────────────────────────            │
│  • Interface IRepository is defined                  │
│  • Multiple implementations exist                    │
│  • DataService depends on IRepository                │
│  • NO decision about which implementation            │
└──────────────────────────────────────────────────────┘
                      │
                      │ Build & Deploy
                      ↓
┌──────────────────────────────────────────────────────┐
│  Deployment Time / Runtime                           │
│  ────────────────────────────────────────            │
│  • Read appsettings.json                             │
│  • See DatabaseProvider = "SqlServer"                │
│  • Bind IRepository → SqlServerRepository            │
│  • Inject into DataService                           │
└──────────────────────────────────────────────────────┘
                      │
                      ↓
┌──────────────────────────────────────────────────────┐
│  To Switch Databases                                 │
│  ────────────────────────────────────────            │
│  1. Edit appsettings.json                            │
│     DatabaseProvider: "Oracle"                       │
│  2. Restart application                              │
│  3. NO RECOMPILATION NEEDED! ✓                       │
└──────────────────────────────────────────────────────┘

8.4 VS Code Setup

In VS Code for .NET, you’d structure your project like this:

MyProject/
├── Program.cs                 (startup & DI configuration)
├── appsettings.json          (runtime configuration)
├── Interfaces/
│   └── IRepository.cs
├── Implementations/
│   ├── SqlServerRepository.cs
│   └── OracleRepository.cs
└── Services/
    └── DataService.cs

Run with:

dotnet run

Change database by editing appsettings.json and restarting—no recompile!

8.5 Real-World Medical Imaging Example

For your radiology work, imagine:

// Late binding for different PACS systems
public interface IPacsClient
{
    void UploadDicomImage(byte[] imageData);
    byte[] DownloadDicomImage(string studyId);
}

// Implementations
public class VnaPacsClient : IPacsClient { ... }
public class GePacsClient : IPacsClient { ... }
public class PhilipsPacsClient : IPacsClient { ... }

// In appsettings.json
{
  "PacsProvider": "VnaPacs"  // Switch hospitals? Just change config!
}

Different hospitals use different PACS systems. With late binding, your radiology AI application can work with any PACS by just changing the configuration file—no code changes needed!