I often use Redis as a distributed cache in my applications. Redis is a key-value store.
I prefer to have my cache keys typed; that is, I don't use plain strings throughout my codebase. Instead, I use objects that are later "translated" into strings.
Here's a basic example:
Suppose we have a cache key that looks like this:
public class MyCacheKey
{
private readonly string _key;
public MyCacheKey()
{
_key = $"{Prefix}-{Key}-{Version}";
}
public required string Prefix { get; init; }
public required string Key { get; init; }
public required int Version {get; init; }
public string GetKey()
{
return _key;
}
}
public interface IMyCache
{
Task<T> Get<T>(MyCacheKey myCacheKey);
}
public class RedisMyCache : IMyCache
{
private readonly IDatabase _database;
public RedisMyCache(IDatabase database)
{
_database = database;
}
public async Task<T> Get<T>(MyCacheKey myCacheKey)
{
var value = await db.StringGetAsync(myCacheKey.GetKey());
// Do something with the string and return it
......
}
}
The StringGetAsync method takes a string, so we need to call the GetKey method to retrieve the string representation of MyCacheKey.
But wouldn't it be nice if we could skip that GetKey call?
So instead of writing this:
var value = await db.StringGetAsync(myCacheKey.GetKey());
We could simply write:
var value = await db.StringGetAsync(myCacheKey);
Implicit operator
We can achieve this by using the implicit operator.
Let's update the MyCacheKey
class:
public class MyCacheKey
{
private readonly string _key;
public MyCacheKey()
{
_key = $"{Prefix}-{Key}-{Version}";
}
public required string Prefix { get; init; }
public required string Key { get; init; }
public required int Version {get; init; }
public static implicit operator string(MyCacheKey key) => key._key;
}