Skip to content

Commit

Permalink
Refactor GetOrAdd methods to reduce duplication
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-jitbit committed Nov 5, 2024
1 parent 1149517 commit 262a663
Showing 1 changed file with 14 additions and 46 deletions.
60 changes: 14 additions & 46 deletions FastCache/FastCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,33 +173,36 @@ public bool TryAdd(TKey key, TValue value, TimeSpan ttl)
return _dict.TryAdd(key, new TtlValue(value, ttl));
}

/// <summary>
/// Adds a key/value pair by using the specified function if the key does not already exist, or returns the existing value if the key exists.
/// </summary>
/// <param name="key">The key to add</param>
/// <param name="valueFactory">The factory function used to generate the item for the key</param>
/// <param name="ttl">TTL of the item</param>
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory, TimeSpan ttl)
private TValue GetOrAddCore(TKey key, Func<TValue> valueFactory, TimeSpan ttl)
{
bool wasAdded = false; //flag to indicate "add vs get". TODO: wrap in ref type some day to avoid captures/closures
var ttlValue = _dict.GetOrAdd(
key,
(k) =>
{
wasAdded = true;
return new TtlValue(valueFactory(k), ttl);
return new TtlValue(valueFactory(), ttl);
});

//if the item is expired, update value and TTL
//since TtlValue is a reference type we can update its properties in-place, instead of removing and re-adding to the dictionary (extra lookups)
if (!wasAdded) //performance hack: skip expiration check if a brand item was just added
{
ttlValue.ModifyIfExpired(() => valueFactory(key), ttl);
ttlValue.ModifyIfExpired(valueFactory, ttl);
}

return ttlValue.Value;
}

/// <summary>
/// Adds a key/value pair by using the specified function if the key does not already exist, or returns the existing value if the key exists.
/// </summary>
/// <param name="key">The key to add</param>
/// <param name="valueFactory">The factory function used to generate the item for the key</param>
/// <param name="ttl">TTL of the item</param>
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory, TimeSpan ttl)
=> GetOrAddCore(key, () => valueFactory(key), ttl);

/// <summary>
/// Adds a key/value pair by using the specified function if the key does not already exist, or returns the existing value if the key exists.
/// </summary>
Expand All @@ -208,25 +211,7 @@ public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory, TimeSpan ttl)
/// <param name="ttl">TTL of the item</param>
/// <param name="factoryArgument">Argument value to pass into valueFactory</param>
public TValue GetOrAdd<TArg>(TKey key, Func<TKey, TArg, TValue> valueFactory, TimeSpan ttl, TArg factoryArgument)
{
bool wasAdded = false; //flag to indicate "add vs get"
var ttlValue = _dict.GetOrAdd(
key,
(k) =>
{
wasAdded = true;
return new TtlValue(valueFactory(k, factoryArgument), ttl);
});

//if the item is expired, update value and TTL
//since TtlValue is a reference type we can update its properties in-place, instead of removing and re-adding to the dictionary (extra lookups)
if (!wasAdded) //performance hack: skip expiration check if a brand item was just added
{
ttlValue.ModifyIfExpired(() => valueFactory(key, factoryArgument), ttl);
}

return ttlValue.Value;
}
=> GetOrAddCore(key, () => valueFactory(key, factoryArgument), ttl);

/// <summary>
/// Adds a key/value pair by using the specified function if the key does not already exist, or returns the existing value if the key exists.
Expand All @@ -235,24 +220,7 @@ public TValue GetOrAdd<TArg>(TKey key, Func<TKey, TArg, TValue> valueFactory, Ti
/// <param name="value">The value to add</param>
/// <param name="ttl">TTL of the item</param>
public TValue GetOrAdd(TKey key, TValue value, TimeSpan ttl)
{
bool wasAdded = false; //flag to indicate "add vs get"
var ttlValue = _dict.GetOrAdd(key,
(k) =>
{
wasAdded = true;
return new TtlValue(value, ttl);
});

//if the item is expired, update value and TTL
//since TtlValue is a reference type we can update its properties in-place, instead of removing and re-adding to the dictionary (extra lookups)
if (!wasAdded) //performance hack: skip expiration check if a brand item was just added
{
ttlValue.ModifyIfExpired(() => value, ttl);
}

return ttlValue.Value;
}
=> GetOrAddCore(key, () => value, ttl);

/// <summary>
/// Tries to remove item with the specified key
Expand Down

0 comments on commit 262a663

Please sign in to comment.