Thanks to Martin, who took some time to view my blog entries, he suggested me to use ConditionalWeakTable<TKey, TValue> to get rid of the object disposing notification and to shorten the Dictionary-cascade to store values with the usage of parenthesis for dictionary key.
One more word to ConditionalWeakTable<TKey, TValue>: This class from compiler services was unknown to me, until its use for the mentioned improvement for the persistent variables for method scope only stuff. However, explained in short words, when the garbage collector checks for object removal, he ignores the key reference in ConditionalWeakTable instances, and with the removal of the specific object, the entry in in the weak table loses references as well and will be collected by garbage collector as well sooner or later (except your code has any other references to TValue, of course). This provides a nice way to mount values on other objects while having an insurance, that these values will die together with the target object ...
Okay, here's the Persistent class again, with the Stack Frame Offset approach:
public static class Persistent<T> where T : struct { #region StaticValues static readonly Dictionary<int, T[]> staticValues; public static ref T Static(T value) { var stackFrameOffset = new StackFrame(1, false).GetNativeOffset(); if (!staticValues.ContainsKey(stackFrameOffset)) staticValues.Add(stackFrameOffset, new T[] { value }); return ref staticValues[stackFrameOffset][0]; } #endregion #region NonStaticValues static readonly ConditionalWeakTable<object, Dictionary<int, T[]>> nonStaticValues; public static ref T Local(T value, object callerInstance) { var stackFrameOffset = new StackFrame(1, false).GetNativeOffset(); if (!nonStaticValues.TryGetValue(callerInstance, out var storage)) { storage = new Dictionary<int, T[]> { { stackFrameOffset, new T[] {value} } }; nonStaticValues.Add(callerInstance, storage); } else if (!storage.ContainsKey(stackFrameOffset)) { storage.Add(stackFrameOffset, new T[] { value }); } return ref storage[stackFrameOffset][0]; } #endregion static Persistent() { staticValues = new Dictionary<int, T[]>(); nonStaticValues = new ConditionalWeakTable<object, Dictionary<int, T[]>>(); } }
And here with the default compiler arguments approach:
public static class Persistent<T> where T : struct { #region StaticValues static readonly Dictionary<(string callerFilePath, string callerMemberName, int callerLineNumber), T[]> staticValues; public static ref T Static(T value, [CallerFilePath] string callerFilePath = default, [CallerMemberName] string callerMemberName = default, [CallerLineNumber] int callerLineNumber = default) { var complierKeys = (callerFilePath, callerMemberName, callerLineNumber); if (!staticValues.ContainsKey(complierKeys)) staticValues.Add(complierKeys, new T[] { value }); return ref staticValues[complierKeys][0]; } #endregion #region NonStaticValues static readonly ConditionalWeakTable<object, Dictionary<(string callerFilePath, string callerMemberName, int callerLineNumber), T[]>> nonStaticValues; public static ref T Local(T value, object callerInstance, [CallerFilePath] string callerFilePath = default, [CallerMemberName] string callerMemberName = default, [CallerLineNumber] int callerLineNumber = default) { var complierKeys = (callerFilePath, callerMemberName, callerLineNumber); if (!nonStaticValues.TryGetValue(callerInstance, out var storage)) { storage = new Dictionary<(string callerFilePath, string callerMemberName, int callerFileNumber), T[]> { { complierKeys, new T[] {value} } }; nonStaticValues.Add(callerInstance, storage); } else if (!storage.ContainsKey(complierKeys)) { storage.Add(complierKeys, new T[] { value }); } return ref storage[complierKeys][0]; } #endregion static Persistent() { staticValues = new Dictionary<(string callerFilePath, string callerMemberName, int callerLineNumber), T[]>(); nonStaticValues = new ConditionalWeakTable<object, Dictionary<(string callerFilePath, string callerMemberName, int callerLineNumber), T[]>>(); } }
The usage hasn't changed except you don't need to implement any interface anymore when using non-static persistent values in your methods.
And still, bricolage ;)