Skip to content

Instantly share code, notes, and snippets.

@theodorzoulias
Last active April 19, 2024 12:24
Show Gist options
  • Save theodorzoulias/46936d9c04fb45630a0d6782d6ce2546 to your computer and use it in GitHub Desktop.
Save theodorzoulias/46936d9c04fb45630a0d6782d6ce2546 to your computer and use it in GitHub Desktop.
JsonKeyedCollectionBase for JSON serialization/deserialization
// Related: https://github.com/dotnet/runtime/issues/101003 -- [API Proposal]: New method HashSet<T>.Add that replaces an existing element if found
abstract class JsonKeyedCollectionBase<TKey, TItem> : ICollection<TItem>, IEqualityComparer<TItem>
{
private readonly HashSet<TItem> _set;
protected abstract TKey GetKeyForItem(TItem item);
protected abstract TItem GetItemForKey(TKey key);
protected virtual IEqualityComparer<TKey> KeyComparer => EqualityComparer<TKey>.Default;
protected virtual IComparer<TItem> EnumerationComparer => null;
protected JsonKeyedCollectionBase()
{
_set = new((IEqualityComparer<TItem>)this);
}
public bool TryGetItem(TKey key, out TItem item) => _set.TryGetValue(GetItemForKey(key), out item);
public void Add(TItem item)
{
if (_set.Add(item)) return;
bool removed = _set.Remove(item);
bool added = _set.Add(item);
Debug.Assert(removed);
Debug.Assert(added);
}
public bool Remove(TKey key) => _set.Remove(GetItemForKey(key));
public IEnumerator<TItem> GetEnumerator()
{
IComparer<TItem> comparer = EnumerationComparer;
return comparer is null ? _set.GetEnumerator() : _set.Order(comparer).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
bool IEqualityComparer<TItem>.Equals(TItem x, TItem y) => KeyComparer.Equals(GetKeyForItem(x), GetKeyForItem(y));
int IEqualityComparer<TItem>.GetHashCode(TItem x) => KeyComparer.GetHashCode(GetKeyForItem(x));
private ICollection<TItem> Collection => _set;
int ICollection<TItem>.Count => Collection.Count;
bool ICollection<TItem>.IsReadOnly => Collection.IsReadOnly;
bool ICollection<TItem>.Contains(TItem item) => Collection.Contains(item);
bool ICollection<TItem>.Remove(TItem item) => Collection.Remove(item);
void ICollection<TItem>.Clear() => Collection.Clear();
void ICollection<TItem>.CopyTo(TItem[] array, int arrayIndex) => Collection.CopyTo(array, arrayIndex);
}
/*
// Usage example
readonly record struct MediaRecord(string FileName, DateTime TimeStamp, double Volume, double SpeedRatio);
class JsonMediaCollection : JsonKeyedCollectionBase<string, MediaRecord>
{
private static readonly Comparer<MediaRecord> s_EnumerationComparer
= Comparer<MediaRecord>.Create((x, y) => x.TimeStamp.CompareTo(y.TimeStamp));
protected override MediaRecord GetItemForKey(string key) => default(MediaRecord) with { FileName = key };
protected override string GetKeyForItem(MediaRecord item) => item.FileName;
protected override IEqualityComparer<string> KeyComparer => StringComparer.OrdinalIgnoreCase;
protected override IComparer<MediaRecord> EnumerationComparer => s_EnumerationComparer;
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment