Repository: dotnet/aspnetcore
File: src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs (C#)

Code before:
            SetEntry(key, value, tags, validFor, entryId);
        }

        return ValueTask.CompletedTask;
    }

    private void SetEntry(string key, byte[] value, string[]? tags, TimeSpan validFor, Guid entryId)
    {
        Debug.Assert(key != null);

        var options = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = validFor,
            Size = value.Length
        };

        if (tags is { Length: > 0 })
        {
            // Remove cache keys from tag lists when the entry is evicted
            options.RegisterPostEvictionCallback(RemoveFromTags, ValueTuple.Create(tags, entryId));
        }

        _cache.Set(key, value, options);
    }

    void RemoveFromTags(object key, object? value, EvictionReason reason, object? state)
    {
        Debug.Assert(state != null);

        var (tags, entryId) = ((string[] tags, Guid entryId))state;

        Debug.Assert(tags != null);
        Debug.Assert(tags.Length > 0);
        Debug.Assert(key is string);
        Debug.Assert(entryId != Guid.Empty);

        lock (_tagsLock)
        {
            foreach (var tag in tags)
            {
                if (_taggedEntries.TryGetValue(tag, out var tagged))
                {
                    tagged.Remove((key: (string)key, entryId));

                    // Remove the collection if there is no more keys in it
                    if (tagged.Count == 0)
                    {
                        _taggedEntries.Remove(tag);
                    }
                }
            }

Review comment (suggestion):
```suggestion
    private void RemoveFromTags(object key, object? value, EvictionReason reason, object? state)
```
While you're on it (same as above, it should be consistent, so either have them all or none (I prefer to have them)).

Code after:
            SetEntry(key, value, tags, validFor, entryId);
        }

        return ValueTask.CompletedTask;
    }

    private void SetEntry(string key, byte[] value, string[]? tags, TimeSpan validFor, Guid entryId)
    {
        Debug.Assert(key != null);

        var options = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = validFor,
            Size = value.Length
        };

        if (tags is { Length: > 0 })
        {
            // Remove cache keys from tag lists when the entry is evicted
            options.RegisterPostEvictionCallback(RemoveFromTags, (tags, entryId));
        }

        _cache.Set(key, value, options);
    }

    private void RemoveFromTags(object key, object? value, EvictionReason reason, object? state)
    {
        Debug.Assert(state != null);

        var (tags, entryId) = ((string[] Tags, Guid EntryId))state;

        Debug.Assert(tags != null);
        Debug.Assert(tags.Length > 0);
        Debug.Assert(key is string);
        Debug.Assert(entryId != Guid.Empty);

        lock (_tagsLock)
        {
            foreach (var tag in tags)
            {
                if (_taggedEntries.TryGetValue(tag, out var tagged))
                {
                    tagged.Remove(new TaggedEntry((string)key, entryId));

                    // Remove the collection if there is no more keys in it
                    if (tagged.Count == 0)
                    {
                        _taggedEntries.Remove(tag);
                    }
                }
            }