DotNext by Roman Sakno

<PackageReference Include="DotNext" Version="1.2.2" />

 UserDataSlot<V>

public struct UserDataSlot<V> : IEquatable<UserDataSlot<V>>
Uniquely identifies user data which can be associated with any object.
using System; using System.Collections.Generic; namespace DotNext { public readonly struct UserDataSlot<V> : IEquatable<UserDataSlot<V>> { private readonly long id; private UserDataSlot(long id) { this.id = id; } public static UserDataSlot<V> Allocate() { return new UserDataSlot<V>(UserDataSlot.NewId); } internal V GetUserData(IDictionary<long, object> storage, V defaultValue) { if (!storage.TryGetValue(id, out object value) || !(value is V)) return defaultValue; return (V)value; } internal bool GetUserData(IDictionary<long, object> storage, out V userData) { if (storage.TryGetValue(id, out object value) && value is V) { V val = userData = (V)value; return true; } userData = default(V); return false; } internal void SetUserData(IDictionary<long, object> storage, V userData) { if (id == 0) throw new ArgumentException(ExceptionMessages.InvalidUserDataSlot); storage[id] = userData; } internal bool RemoveUserData(IDictionary<long, object> storage) { return storage.Remove(id); } public bool Equals(UserDataSlot<V> other) { return id == other.id; } public override bool Equals(object other) { if (other is UserDataSlot<V>) { UserDataSlot<V> other2 = (UserDataSlot<V>)other; return Equals(other2); } return false; } public override int GetHashCode() { return id.GetHashCode(); } public override string ToString() { return id.ToString((IFormatProvider)null); } public static bool operator ==(UserDataSlot<V> first, UserDataSlot<V> second) { return first.id == second.id; } public static bool operator !=(UserDataSlot<V> first, UserDataSlot<V> second) { return first.id != second.id; } } }