On dotnet runtime Github repo there was a proposal for adding HashSet.GetOrAdd(T)
- similar to
public static T GetOrAdd<T>(this HashSet<T> hashSet, T equalValue)
{
if (hashSet.TryGetValue(equalValue, out var actualValue))
return actualValue;
hashSet.Add(equalValue);
return equalValue;
}
but without a duplicate hash lookup (performance impact).
That issue was closed with the following resolution:
We're going down CollectionsMarshal route for #15059, we should probably consider a similar approach for HashSet
In issue #15059 the last comment has an example of CollectionsMarshal usage for a Dictionary:
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory)
where TKey : notnull
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
if (valueFactory == null)
throw new ArgumentNullException(nameof(valueFactory));
ref TValue? value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out bool exists);
if (!exists)
value = valueFactory(key);
return value!;
}
I've tried to use the same approach to implement HashSet.GetOrAdd(T)
extension but failed - I don't have any idea how to get key value for CollectionsMarshal.GetValueRefOrAddDefault()
method.
Both issues are locked as resolved and limited to collaborators - so I cannot ask directly there.
Answer from MS (only creating an extension method that performs double lookup is possible):
For the record, using CollectionsMarshal for HashSet was proven to be not viable because you can't do ref returns on keys - #82840 (comment)
That being said, consensus is that a GetOrAdd method for HashSet is not particularly valuable compared to just using HashSet.Add except for the rare scenario where you need to access the actual instance stored in the HashSet - #82875 (comment)