Skip to content

Commit 0262045

Browse files
authored
Cache implements IEnumerable<KeyValuePair<K, V>> (#114)
* enumerator * classic
1 parent 3efdce1 commit 0262045

File tree

4 files changed

+105
-2
lines changed

4 files changed

+105
-2
lines changed

BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text;
77
using System.Threading.Tasks;
88
using Xunit;
9+
using System.Collections;
910

1011
namespace BitFaster.Caching.UnitTests.Lru
1112
{
@@ -65,6 +66,26 @@ public void WhenItemsAddedKeysContainsTheKeys()
6566
lru.Keys.Should().BeEquivalentTo(new[] { 1, 2 });
6667
}
6768

69+
[Fact]
70+
public void WhenItemsAddedGenericEnumerateContainsKvps()
71+
{
72+
lru.Count.Should().Be(0);
73+
lru.GetOrAdd(1, valueFactory.Create);
74+
lru.GetOrAdd(2, valueFactory.Create);
75+
lru.Should().BeEquivalentTo(new[] { new KeyValuePair<int, string>(1, "1"), new KeyValuePair<int, string>(2, "2") });
76+
}
77+
78+
[Fact]
79+
public void WhenItemsAddedEnumerateContainsKvps()
80+
{
81+
lru.Count.Should().Be(0);
82+
lru.GetOrAdd(1, valueFactory.Create);
83+
lru.GetOrAdd(2, valueFactory.Create);
84+
85+
var enumerable = (IEnumerable)lru;
86+
enumerable.Should().BeEquivalentTo(new[] { new KeyValuePair<int, string>(1, "1"), new KeyValuePair<int, string>(2, "2") });
87+
}
88+
6889
[Fact]
6990
public void WhenItemExistsTryGetReturnsValueAndTrue()
7091
{

BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Threading.Tasks;
88
using Xunit;
99
using Xunit.Abstractions;
10+
using System.Collections;
1011

1112
namespace BitFaster.Caching.UnitTests.Lru
1213
{
@@ -117,6 +118,26 @@ public void WhenItemsAddedKeysContainsTheKeys()
117118
lru.Keys.Should().BeEquivalentTo(new[] { 1, 2 });
118119
}
119120

121+
[Fact]
122+
public void WhenItemsAddedGenericEnumerateContainsKvps()
123+
{
124+
lru.Count.Should().Be(0);
125+
lru.GetOrAdd(1, valueFactory.Create);
126+
lru.GetOrAdd(2, valueFactory.Create);
127+
lru.Should().BeEquivalentTo(new[] { new KeyValuePair<int, string>(1, "1"), new KeyValuePair<int, string>(2, "2") });
128+
}
129+
130+
[Fact]
131+
public void WhenItemsAddedEnumerateContainsKvps()
132+
{
133+
lru.Count.Should().Be(0);
134+
lru.GetOrAdd(1, valueFactory.Create);
135+
lru.GetOrAdd(2, valueFactory.Create);
136+
137+
var enumerable = (IEnumerable)lru;
138+
enumerable.Should().BeEquivalentTo(new[] { new KeyValuePair<int, string>(1, "1"), new KeyValuePair<int, string>(2, "2") });
139+
}
140+
120141
[Fact]
121142
public void WhenItemExistsTryGetReturnsValueAndTrue()
122143
{

BitFaster.Caching/Lru/ClassicLru.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Concurrent;
34
using System.Collections.Generic;
45
using System.Linq;
@@ -17,7 +18,7 @@ namespace BitFaster.Caching.Lru
1718
/// </remarks>
1819
/// <typeparam name="K">The type of the key</typeparam>
1920
/// <typeparam name="V">The type of the value</typeparam>
20-
public sealed class ClassicLru<K, V> : ICache<K, V>
21+
public sealed class ClassicLru<K, V> : ICache<K, V>, IEnumerable<KeyValuePair<K, V>>
2122
{
2223
private readonly int capacity;
2324
private readonly ConcurrentDictionary<K, LinkedListNode<LruItem>> dictionary;
@@ -59,6 +60,22 @@ public ClassicLru(int concurrencyLevel, int capacity, IEqualityComparer<K> compa
5960
/// </summary>
6061
public ICollection<K> Keys => this.dictionary.Keys;
6162

63+
/// <summary>Returns an enumerator that iterates through the cache.</summary>
64+
/// <returns>An enumerator for the cache.</returns>
65+
/// <remarks>
66+
/// The enumerator returned from the cache is safe to use concurrently with
67+
/// reads and writes, however it does not represent a moment-in-time snapshot.
68+
/// The contents exposed through the enumerator may contain modifications
69+
/// made after <see cref="GetEnumerator"/> was called.
70+
/// </remarks>
71+
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
72+
{
73+
foreach (var kvp in this.dictionary)
74+
{
75+
yield return new KeyValuePair<K, V>(kvp.Key, kvp.Value.Value.Value);
76+
}
77+
}
78+
6279
///<inheritdoc/>
6380
public bool TryGet(K key, out V value)
6481
{
@@ -289,6 +306,19 @@ private void LockAndMoveToEnd(LinkedListNode<LruItem> node)
289306
}
290307
}
291308

309+
/// <summary>Returns an enumerator that iterates through the cache.</summary>
310+
/// <returns>An enumerator for the cache.</returns>
311+
/// <remarks>
312+
/// The enumerator returned from the cache is safe to use concurrently with
313+
/// reads and writes, however it does not represent a moment-in-time snapshot.
314+
/// The contents exposed through the enumerator may contain modifications
315+
/// made after <see cref="GetEnumerator"/> was called.
316+
/// </remarks>
317+
IEnumerator IEnumerable.GetEnumerator()
318+
{
319+
return ((ClassicLru<K, V>)this).GetEnumerator();
320+
}
321+
292322
private class LruItem
293323
{
294324
public LruItem(K k, V v)

BitFaster.Caching/Lru/TemplateConcurrentLru.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Concurrent;
34
using System.Collections.Generic;
45
using System.Linq;
@@ -27,7 +28,7 @@ namespace BitFaster.Caching.Lru
2728
/// 5. When warm is full, warm tail is moved to warm head or cold depending on WasAccessed.
2829
/// 6. When cold is full, cold tail is moved to warm head or removed from dictionary on depending on WasAccessed.
2930
/// </remarks>
30-
public class TemplateConcurrentLru<K, V, I, P, T> : ICache<K, V>
31+
public class TemplateConcurrentLru<K, V, I, P, T> : ICache<K, V>, IEnumerable<KeyValuePair<K, V>>
3132
where I : LruItem<K, V>
3233
where P : struct, IItemPolicy<K, V, I>
3334
where T : struct, ITelemetryPolicy<K, V>
@@ -101,6 +102,23 @@ public TemplateConcurrentLru(
101102
/// </summary>
102103
public ICollection<K> Keys => this.dictionary.Keys;
103104

105+
106+
/// <summary>Returns an enumerator that iterates through the cache.</summary>
107+
/// <returns>An enumerator for the cache.</returns>
108+
/// <remarks>
109+
/// The enumerator returned from the cache is safe to use concurrently with
110+
/// reads and writes, however it does not represent a moment-in-time snapshot.
111+
/// The contents exposed through the enumerator may contain modifications
112+
/// made after <see cref="GetEnumerator"/> was called.
113+
/// </remarks>
114+
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
115+
{
116+
foreach (var kvp in this.dictionary)
117+
{
118+
yield return new KeyValuePair<K, V>(kvp.Key, kvp.Value.Value);
119+
}
120+
}
121+
104122
///<inheritdoc/>
105123
public bool TryGet(K key, out V value)
106124
{
@@ -464,5 +482,18 @@ private static (int hot, int warm, int cold) ComputeQueueCapacity(int capacity)
464482

465483
return (hotCapacity, warmCapacity, coldCapacity);
466484
}
485+
486+
/// <summary>Returns an enumerator that iterates through the cache.</summary>
487+
/// <returns>An enumerator for the cache.</returns>
488+
/// <remarks>
489+
/// The enumerator returned from the cache is safe to use concurrently with
490+
/// reads and writes, however it does not represent a moment-in-time snapshot.
491+
/// The contents exposed through the enumerator may contain modifications
492+
/// made after <see cref="GetEnumerator"/> was called.
493+
/// </remarks>
494+
IEnumerator IEnumerable.GetEnumerator()
495+
{
496+
return ((TemplateConcurrentLru<K, V, I, P, T>)this).GetEnumerator();
497+
}
467498
}
468499
}

0 commit comments

Comments
 (0)