Skip to content

Commit f416aeb

Browse files
committed
Add link restoring method to SyncValue and automatically dispose them from sync object
1 parent 1e55843 commit f416aeb

File tree

2 files changed

+98
-21
lines changed

2 files changed

+98
-21
lines changed

MonkeyLoader/Sync/MonkeySyncObject.cs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using EnumerableToolkit;
22
using HarmonyLib;
33
using MonkeyLoader.Meta;
4-
using MonkeyLoader.Patching;
54
using System;
65
using System.Collections.Generic;
76
using System.ComponentModel;
@@ -100,6 +99,11 @@ public abstract class MonkeySyncObject<TSyncObject, TSyncValue, TLink> : IUnlink
10099
/// </summary>
101100
protected static readonly Dictionary<string, Func<TSyncObject, TSyncValue>> propertyAccessorsByName = new(StringComparer.Ordinal);
102101

102+
/// <summary>
103+
/// The <typeparamref name="TSyncValue"/> instances associated with this sync object.
104+
/// </summary>
105+
protected readonly HashSet<TSyncValue> syncValues = [];
106+
103107
private bool _disposedValue;
104108

105109
/// <inheritdoc/>
@@ -217,19 +221,26 @@ protected virtual bool EstablishLink(bool fromRemote)
217221
/// Creates a link for the given sync value of the given name.
218222
/// </summary>
219223
/// <remarks>
220-
/// <i>By default:</i> Calls
221-
/// <c><paramref name="syncValue"/>.<see cref="IUnlinkedMonkeySyncValue{TLink}.EstablishLinkFor">EstablishLinkFor</see>()</c>.
224+
/// <i>By default:</i> Adds the given sync value to the <see cref="syncValues">set of instances</see> and calls
225+
/// <c><paramref name="syncValue"/>.<see cref="IUnlinkedMonkeySyncValue{TLink}.EstablishLinkFor">EstablishLinkFor</see>()</c>.
222226
/// </remarks>
223227
/// <param name="syncValue">The sync value to link.</param>
224228
/// <param name="propertyName">The name of the sync value to link.</param>
225229
/// <param name="fromRemote">Whether the link is being established from the remote side.</param>
226230
/// <returns><c>true</c> if the link was successfully created; otherwise, <c>false</c>.</returns>
227231
protected virtual bool EstablishLinkFor(TSyncValue syncValue, string propertyName, bool fromRemote)
228-
=> syncValue.EstablishLinkFor(this, propertyName, fromRemote);
232+
{
233+
syncValues.Add(syncValue);
234+
return syncValue.EstablishLinkFor(this, propertyName, fromRemote);
235+
}
229236

230237
/// <summary>
231238
/// Creates a link for the given sync method of the given name.
232239
/// </summary>
240+
/// <remarks>
241+
/// Any <typeparamref name="TSyncValue"/>s created for this
242+
/// must be added to the <see cref="syncValues">set of instances</see>.
243+
/// </remarks>
233244
/// <param name="syncMethod">The sync method to link.</param>
234245
/// <param name="methodName">The name of the sync method to link.</param>
235246
/// <param name="fromRemote">Whether the link is being established from the remote side.</param>
@@ -240,10 +251,15 @@ protected virtual bool EstablishLinkFor(TSyncValue syncValue, string propertyNam
240251
/// Cleans up any managed resources as part of <see cref="Dispose()">disposing</see>.
241252
/// </summary>
242253
/// <remarks>
243-
/// <i>By default:</i> Disposes the <see cref="LinkObject">LinkObject</see> if it's <see cref="IDisposable"/>.
254+
/// <i>By default:</i> Disposes all <typeparamref name="TSyncValue"/> instances
255+
/// that were added to the <see cref="syncValues">set of instances</see>,
256+
/// and the <see cref="LinkObject">LinkObject</see> if it's <see cref="IDisposable"/>.
244257
/// </remarks>
245258
protected virtual void OnDisposing()
246259
{
260+
foreach (var syncValue in syncValues)
261+
syncValue.Dispose();
262+
247263
if (LinkObject is IDisposable disposable)
248264
disposable.Dispose();
249265
}
@@ -294,9 +310,9 @@ protected void OnPropertyChanged(string propertyName)
294310
}
295311

296312
/// <remarks><para>
297-
/// <i>By default:</i> Calls <see cref="TryRestoreLinkFor(TSyncValue, string)">TryRestoreLinkFor</see>
313+
/// <i>By default:</i> Calls <see cref="TryRestoreLinkFor(TSyncValue)">TryRestoreLinkFor</see>
298314
/// for every readable <typeparamref name="TSyncValue"/> instance property and
299-
/// <see cref="TryRestoreLinkFor(TSyncValue, string)">its overload</see> for every
315+
/// <see cref="TryRestoreLinkFor(Action{TSyncObject}, string)">its overload</see> for every
300316
/// <see cref="MonkeySyncMethodAttribute">MonkeySync method</see> on <typeparamref name="TSyncObject"/>.<br/>
301317
/// The detected properties are stored in <see cref="propertyAccessorsByName">propertyAccessorsByName</see>,
302318
/// while the detected methods are stored in <see cref="methodsByName">methodsByName</see>.
@@ -312,24 +328,26 @@ protected virtual bool TryRestoreLink()
312328
{
313329
var success = true;
314330

315-
foreach (var syncValueProperty in propertyAccessorsByName)
316-
success &= TryRestoreLinkFor(syncValueProperty.Value((TSyncObject)this), syncValueProperty.Key);
331+
foreach (var syncValues in propertyAccessorsByName.Values)
332+
success &= TryRestoreLinkFor(syncValues((TSyncObject)this));
317333

318334
foreach (var syncMethod in methodsByName)
319335
success &= TryRestoreLinkFor(syncMethod.Value, syncMethod.Key);
320336

321337
return success;
322338
}
323339

324-
// Implement the sync value one through a method on sync values
325-
326340
/// <summary>
327-
/// Tries to restore the link for the given sync value of the given name.
341+
/// Tries to restore the link for the given sync value.
328342
/// </summary>
343+
/// <remarks>
344+
/// <i>By default:</i> Calls
345+
/// <c><paramref name="syncValue"/>.<see cref="ILinkedMonkeySyncValue{TLink}.TryRestoreLink">TryRestoreLink</see>()</c>.
346+
/// </remarks>
329347
/// <param name="syncValue">The sync value to link.</param>
330-
/// <param name="propertyName">The name of the sync value to link.</param>
331348
/// <returns><c>true</c> if the link was successfully restored; otherwise, <c>false</c>.</returns>
332-
protected abstract bool TryRestoreLinkFor(TSyncValue syncValue, string propertyName);
349+
protected virtual bool TryRestoreLinkFor(TSyncValue syncValue)
350+
=> syncValue.TryRestoreLink();
333351

334352
/// <summary>
335353
/// Tries to restore the link for the given sync method of the given name.

MonkeyLoader/Sync/MonkeySyncValue.cs

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ namespace MonkeyLoader.Sync
1313
/// Defines the non-generic interface for <see cref="ILinkedMonkeySyncValue{TLink, T}"/>s.
1414
/// </summary>
1515
/// <inheritdoc cref="ILinkedMonkeySyncValue{TLink, T}"/>
16-
public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged
16+
public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged, IDisposable
1717
{
18+
/// <summary>
19+
/// Gets the <see cref="MonkeySyncObject{TSyncObject, TSyncValue, TLink}.LinkObject">LinkObject</see>
20+
/// of the <see cref="ILinkedMonkeySyncValue{TLink}.SyncObject">SyncObject</see> that this value belongs to.
21+
/// </summary>
22+
public TLink LinkObject { get; }
23+
1824
/// <summary>
1925
/// Gets the property name of this sync value.
2026
/// </summary>
@@ -35,6 +41,12 @@ public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged
3541
/// the wrapped <see cref="Value">Value</see>.
3642
/// </summary>
3743
public Type ValueType { get; }
44+
45+
/// <summary>
46+
/// Tries to restore the link of this sync value.
47+
/// </summary>
48+
/// <returns><c>true</c> if the link was successfully restored; otherwise, <c>false</c>.</returns>
49+
public bool TryRestoreLink();
3850
}
3951

4052
/// <summary>
@@ -45,12 +57,6 @@ public interface ILinkedMonkeySyncValue<out TLink> : INotifyValueChanged
4557
public interface ILinkedMonkeySyncValue<out TLink, T> : INotifyValueChanged<T>,
4658
IReadOnlyMonkeySyncValue<TLink, T>, IWriteOnlyMonkeySyncValue<TLink, T>
4759
{
48-
/// <summary>
49-
/// Gets the <see cref="MonkeySyncObject{TSyncObject, TSyncValue, TLink}.LinkObject">LinkObject</see>
50-
/// of the <see cref="ILinkedMonkeySyncValue{TLink}.SyncObject">SyncObject</see> that this value belongs to.
51-
/// </summary>
52-
public TLink LinkObject { get; }
53-
5460
/// <inheritdoc cref="ILinkedMonkeySyncValue{TLink}.Value"/>
5561
public new T Value { get; set; }
5662
}
@@ -109,6 +115,7 @@ public abstract class MonkeySyncValue<TLink, T> : IUnlinkedMonkeySyncValue<TLink
109115
{
110116
private static readonly Type _valueType = typeof(T);
111117

118+
private bool _disposedValue;
112119
private ValueChangedEventHandler? _untypedChanged;
113120
private T _value;
114121

@@ -154,12 +161,28 @@ public MonkeySyncValue(T value)
154161
Value = value;
155162
}
156163

164+
/// <summary>
165+
/// Ensures any unmanaged resources are <see cref="Dispose(bool)">disposed</see>.
166+
/// </summary>
167+
~MonkeySyncValue()
168+
{
169+
Dispose(false);
170+
}
171+
157172
/// <summary>
158173
/// Unwraps the <see cref="Value">Value</see> from the given sync object.
159174
/// </summary>
160175
/// <param name="syncValue">The sync object to unwrap.</param>
161176
public static implicit operator T(MonkeySyncValue<TLink, T> syncValue) => syncValue.Value;
162177

178+
/// <inheritdoc/>
179+
public void Dispose()
180+
{
181+
// Do not change this code. Put cleanup code in the 'OnDisposing()' or 'OnFinalizing()' methods
182+
Dispose(true);
183+
GC.SuppressFinalize(this);
184+
}
185+
163186
/// <remarks>
164187
/// Sets this sync value's <see cref="SyncObject">SyncObject</see>
165188
/// and <see cref="Name">Name</see> to the ones provided.<br/>
@@ -177,6 +200,9 @@ public bool EstablishLinkFor(ILinkedMonkeySyncObject<TLink> syncObject, string p
177200
/// <inheritdoc/>
178201
public override string ToString() => Value?.ToString() ?? "";
179202

203+
/// <inheritdoc/>
204+
public abstract bool TryRestoreLink();
205+
180206
/// <remarks>
181207
/// Handles the aspects of establishing a link that are
182208
/// particular to <typeparamref name="TLink"/>s as a link object.<br/>
@@ -187,6 +213,39 @@ public bool EstablishLinkFor(ILinkedMonkeySyncObject<TLink> syncObject, string p
187213
/// <inheritdoc cref="IUnlinkedMonkeySyncValue{TLink}.EstablishLinkFor"/>
188214
protected abstract bool EstablishLinkInternal(bool fromRemote);
189215

216+
/// <summary>
217+
/// Cleans up any managed resources as part of <see cref="Dispose()">disposing</see>.
218+
/// </summary>
219+
/// <remarks>
220+
/// <i>By default:</i> Sets the <see cref="SyncObject">SyncObject</see> to <c>null</c>
221+
/// and the internal <see cref="Value">value</see> to its <c>default</c>.
222+
/// </remarks>
223+
protected virtual void OnDisposing()
224+
{
225+
SyncObject = null!;
226+
_value = default!;
227+
}
228+
229+
/// <summary>
230+
/// Cleans up any unmanaged resources as part of
231+
/// <see cref="Dispose()">disposing</see> or finalization.
232+
/// </summary>
233+
protected virtual void OnFinalizing()
234+
{ }
235+
236+
private void Dispose(bool disposing)
237+
{
238+
if (_disposedValue)
239+
return;
240+
241+
if (disposing)
242+
OnDisposing();
243+
244+
OnFinalizing();
245+
246+
_disposedValue = true;
247+
}
248+
190249
/// <summary>
191250
/// Handles the value of this config item potentially having changed.
192251
/// If the <paramref name="oldValue"/> and new value are different:<br/>

0 commit comments

Comments
 (0)