-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Background and motivation
The Dictionary<TKey,TValue> collection was improved in .NET 6, by introducing direct ref access to its values. This improvement made the collection more suitable for performance-critical applications, by reducing the number of times a key has to be hashed when adding/modifying values in the collection. For example the GetOrAdd extension method can be implemented efficiently with a single hashing of the key. The same improvement could be made to the HashSet<T> collection as well. A GetOrAdd API has also been proposed for the HashSet<T>, but that proposal was closed with a mention about adding CollectionsMarshal support in the future. This proposal here is exactly about adding this support.
API Proposal
namespace System.Runtime.InteropServices
{
public static partial class CollectionsMarshal
{
public static ref T? GetValueRefOrAddDefault<T> (HashSet<T> hashSet, T item, out bool exists);
public static ref T GetValueRefOrNullRef<T> (HashSet<T> hashSet, T item);
}
}API Usage
Below is a partial implementation of a concurrent HashSet<T> with bounded capacity. The proposed API GetValueRefOrAddDefault is used when space is available in the collection, and the proposed API GetValueRefOrNullRef is used when the collection is full:
public partial class ConcurrentBoundedHashSet<T>
{
private readonly HashSet<T> _set = new();
private readonly int _boundedCapacity;
/// <summary>
/// Searches the set for a given value and returns the equal value it finds,
/// otherwise adds the given value to the set if space is available,
/// otherwise returns false.
/// </summary>
public bool TryGetOrAdd(T equalValue, out T? actualValue)
{
lock (_set)
{
if (_set.Count < _boundedCapacity)
{
ref T? valueRef = ref CollectionsMarshal.GetValueRefOrAddDefault(_set, equalValue,
out bool exists);
if (!exists) valueRef = equalValue;
actualValue = valueRef; return true;
}
else
{
ref T valueRef = ref CollectionsMarshal.GetValueRefOrNullRef(_set, equalValue);
if (!Unsafe.IsNullRef(ref valueRef))
{
actualValue = valueRef; return true;
}
else
{
actualValue = default; return false;
}
}
}
}
}Alternative Designs
No response
Risks
None that I can think of.
Labels: area-System.Collections / api-suggestion.