@@ -14,15 +14,14 @@ namespace Autofac.Util;
1414internal class FallbackDictionary < TKey , TValue > : IDictionary < TKey , TValue >
1515 where TKey : notnull
1616{
17- /// <summary>
18- /// Storage for local values set in the dictionary.
19- /// </summary>
2017 private readonly IDictionary < TKey , TValue > _localValues = new Dictionary < TKey , TValue > ( ) ;
21-
22- /// <summary>
23- /// The parent dictionary to which values should fall back when not present in the current dictionary.
24- /// </summary>
2518 private readonly IDictionary < TKey , TValue > _parent ;
19+ private readonly FallbackDictionary < TKey , TValue > ? _fallbackParent ;
20+
21+ private int _localVersion ;
22+ private List < TKey > ? _cachedKeys ;
23+ private int _cachedKeysLocalVersion ;
24+ private int _cachedKeysParentVersion ;
2625
2726 /// <summary>
2827 /// Initializes a new instance of the <see cref="FallbackDictionary{TKey, TValue}"/> class
@@ -43,6 +42,7 @@ public FallbackDictionary()
4342 public FallbackDictionary ( IDictionary < TKey , TValue > parent )
4443 {
4544 _parent = parent ?? throw new ArgumentNullException ( nameof ( parent ) ) ;
45+ _fallbackParent = parent as FallbackDictionary < TKey , TValue > ;
4646 }
4747
4848 /// <summary>
@@ -116,7 +116,7 @@ public ICollection<TValue> Values
116116 {
117117 get
118118 {
119- var keys = GetMergedKeysSnapshot ( ) ;
119+ var keys = GetMergedKeysSnapshotInternal ( ) ;
120120 var values = new TValue [ keys . Count ] ;
121121 for ( var i = 0 ; i < keys . Count ; i ++ )
122122 {
@@ -154,6 +154,7 @@ public TValue this[TKey key]
154154 set
155155 {
156156 _localValues [ key ] = value ;
157+ IncrementVersion ( ) ;
157158 }
158159 }
159160
@@ -200,6 +201,7 @@ public void Add(TKey key, TValue value)
200201 }
201202
202203 _localValues . Add ( key , value ) ;
204+ IncrementVersion ( ) ;
203205 }
204206
205207 /// <summary>
@@ -208,6 +210,7 @@ public void Add(TKey key, TValue value)
208210 public void Clear ( )
209211 {
210212 _localValues . Clear ( ) ;
213+ IncrementVersion ( ) ;
211214 }
212215
213216 /// <summary>
@@ -300,7 +303,13 @@ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
300303 /// </remarks>
301304 public bool Remove ( KeyValuePair < TKey , TValue > item )
302305 {
303- return _localValues . Remove ( item ) ;
306+ var removed = _localValues . Remove ( item ) ;
307+ if ( removed )
308+ {
309+ IncrementVersion ( ) ;
310+ }
311+
312+ return removed ;
304313 }
305314
306315 /// <summary>
@@ -318,7 +327,13 @@ public bool Remove(KeyValuePair<TKey, TValue> item)
318327 /// </remarks>
319328 public bool Remove ( TKey key )
320329 {
321- return _localValues . Remove ( key ) ;
330+ var removed = _localValues . Remove ( key ) ;
331+ if ( removed )
332+ {
333+ IncrementVersion ( ) ;
334+ }
335+
336+ return removed ;
322337 }
323338
324339 /// <summary>
@@ -357,6 +372,40 @@ IEnumerator IEnumerable.GetEnumerator()
357372 /// </summary>
358373 /// <returns>A new list containing the ordered unique set of keys.</returns>
359374 private List < TKey > GetMergedKeysSnapshot ( )
375+ {
376+ var snapshot = GetMergedKeysSnapshotInternal ( ) ;
377+
378+ if ( snapshot . Count == 0 )
379+ {
380+ return new List < TKey > ( ) ;
381+ }
382+
383+ return new List < TKey > ( snapshot ) ;
384+ }
385+
386+ private List < TKey > GetMergedKeysSnapshotInternal ( )
387+ {
388+ if ( _fallbackParent is null )
389+ {
390+ return BuildMergedKeyList ( ) ;
391+ }
392+
393+ var parentVersion = _fallbackParent . _localVersion ;
394+ if ( _cachedKeys is { } cache &&
395+ _cachedKeysLocalVersion == _localVersion &&
396+ _cachedKeysParentVersion == parentVersion )
397+ {
398+ return cache ;
399+ }
400+
401+ cache = BuildMergedKeyList ( ) ;
402+ _cachedKeys = cache ;
403+ _cachedKeysLocalVersion = _localVersion ;
404+ _cachedKeysParentVersion = parentVersion ;
405+ return cache ;
406+ }
407+
408+ private List < TKey > BuildMergedKeyList ( )
360409 {
361410 var keys = new List < TKey > ( _localValues . Count + _parent . Count ) ;
362411 keys . AddRange ( _localValues . Keys ) ;
@@ -369,24 +418,26 @@ private List<TKey> GetMergedKeysSnapshot()
369418 }
370419 }
371420
421+ keys . Sort ( ) ;
372422 return keys ;
373423 }
374424
375425 private IEnumerable < KeyValuePair < TKey , TValue > > EnumerateKeyValuePairs ( )
376426 {
377- foreach ( var kvp in _localValues )
427+ foreach ( var key in GetMergedKeysSnapshotInternal ( ) )
378428 {
379- yield return kvp ;
429+ yield return new KeyValuePair < TKey , TValue > ( key , this [ key ] ) ;
380430 }
431+ }
381432
382- foreach ( var parentKvp in _parent )
433+ private void IncrementVersion ( )
434+ {
435+ unchecked
383436 {
384- if ( _localValues . ContainsKey ( parentKvp . Key ) )
385- {
386- continue ;
387- }
388-
389- yield return parentKvp ;
437+ _localVersion ++ ;
390438 }
439+
440+ _cachedKeys = null ;
441+ _cachedKeysLocalVersion = _localVersion ;
391442 }
392443}
0 commit comments