Skip to content

Instantly share code, notes, and snippets.

@musoftware
Created October 3, 2025 10:19
Show Gist options
  • Select an option

  • Save musoftware/39ec4e1e588a0584b2e8a345644ef660 to your computer and use it in GitHub Desktop.

Select an option

Save musoftware/39ec4e1e588a0584b2e8a345644ef660 to your computer and use it in GitHub Desktop.
Generic TTL Cache in C#
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
// Create a cache with a 5-second Time-To-Live.
var weatherCache = new TTLCache(TimeSpan.FromSeconds(5));
string city = "Suez";
// 1. First call is a CACHE MISS and executes the slow method.
string result1 = weatherCache.Execute(
key: $"weather:{city}",
function: () => FetchWeatherData(city)
);
Console.WriteLine(result1);
Console.WriteLine("\nImmediately calling again...");
// 2. Second call is a CACHE HIT and returns instantly.
string result2 = weatherCache.Execute(
key: $"weather:{city}",
function: () => FetchWeatherData(city)
);
Console.WriteLine(result2);
// 3. Wait for the cache item to expire.
Console.WriteLine("\nWaiting 6 seconds for cache to expire...");
await Task.Delay(6000);
// 4. Third call is a CACHE MISS again.
string result3 = weatherCache.Execute(
key: $"weather:{city}",
function: () => FetchWeatherData(city)
);
Console.WriteLine(result3);
}
/// <summary>
/// Simulates a slow network call to an API.
/// </summary>
public static string FetchWeatherData(string city)
{
Console.WriteLine($"--- Making a slow API call for {city}... ---");
Thread.Sleep(2000); // Simulate 2 seconds of network latency
return $"Weather for {city} as of {DateTime.Now:T}: 28°C, Clear Skies";
}
}
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class TTLCache
{
private class CacheItem
{
public object Value { get; }
public DateTime ExpirationTime { get; }
public CacheItem(object value, TimeSpan ttl)
{
Value = value;
ExpirationTime = DateTime.UtcNow.Add(ttl);
}
public bool IsExpired => DateTime.UtcNow >= ExpirationTime;
}
private readonly ConcurrentDictionary<string, CacheItem> _cache = new();
private readonly TimeSpan _ttl;
public TTLCache(TimeSpan ttl)
{
_ttl = ttl;
}
/// <summary>
/// Executes the function, using a cached result if a valid one exists for the given key.
/// </summary>
/// <typeparam name="T">The return type of the function.</typeparam>
/// <param name="key">A unique key to identify this function call.</param>
/// <param name="function">The function to execute if no valid cached item is found.</param>
/// <returns>The result of the function, either from cache or a fresh execution.</returns>
public T Execute<T>(string key, Func<T> function)
{
if (_cache.TryGetValue(key, out var item) && !item.IsExpired)
{
Console.WriteLine($"CACHE HIT for key: '{key}'.");
return (T)item.Value;
}
Console.WriteLine($"CACHE MISS or EXPIRED for key: '{key}'. Executing function.");
T result = function();
_cache[key] = new CacheItem(result, _ttl);
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment