Skip to content

Commit 206ab27

Browse files
committed
**Fix**:
- Fixed the issues of *ObservableDictionary* when subscribing/unsubscribing to actions while removing/adding elements - Fixed the issues of *ObservableList* when subscribing/unsubscribing to actions while removing/adding elements
1 parent c7f8d65 commit 206ab27

File tree

5 files changed

+52
-22
lines changed

5 files changed

+52
-22
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [0.6.5] - 2024-11-20
8+
9+
**Fix**:
10+
- Fixed the issues of *ObservableDictionary* when subscribing/unsubscribing to actions while removing/adding elements
11+
- Fixed the issues of *ObservableList* when subscribing/unsubscribing to actions while removing/adding elements
12+
713
## [0.6.4] - 2024-11-13
814

915
**Fix**:

Runtime/ObservableDictionary.cs

+22-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Collections.ObjectModel;
5+
using System.Linq;
56
using UnityEngine.UIElements;
67

78
// ReSharper disable once CheckNamespace
@@ -275,16 +276,18 @@ public virtual bool Remove(TKey key)
275276

276277
if (ObservableUpdateFlag != ObservableUpdateFlag.UpdateOnly && _keyUpdateActions.TryGetValue(key, out var actions))
277278
{
278-
for (var i = 0; i < actions.Count; i++)
279+
var listCopy = actions.ToList();
280+
for (var i = 0; i < listCopy.Count; i++)
279281
{
280-
actions[i](key, value, default, ObservableUpdateType.Removed);
282+
listCopy[i](key, value, default, ObservableUpdateType.Removed);
281283
}
282284
}
283285
if (ObservableUpdateFlag != ObservableUpdateFlag.KeyUpdateOnly)
284286
{
285-
for (var i = 0; i < _updateActions.Count; i++)
287+
var listCopy = _updateActions.ToList();
288+
for (var i = 0; i < listCopy.Count; i++)
286289
{
287-
_updateActions[i](key, value, default, ObservableUpdateType.Removed);
290+
listCopy[i](key, value, default, ObservableUpdateType.Removed);
288291
}
289292
}
290293

@@ -294,31 +297,34 @@ public virtual bool Remove(TKey key)
294297
/// <inheritdoc />
295298
public virtual void Clear()
296299
{
297-
var dictionary = new Dictionary<TKey, TValue>(Dictionary);
298-
299-
Dictionary.Clear();
300-
301300
if (ObservableUpdateFlag != ObservableUpdateFlag.UpdateOnly)
302301
{
303-
foreach (var data in _keyUpdateActions)
302+
// Create a copy in case that one of the callbacks modifies the list (Ex: removing a subscriber)
303+
var copy = new Dictionary<TKey, IList<Action<TKey, TValue, TValue, ObservableUpdateType>>>(_keyUpdateActions);
304+
305+
foreach (var data in copy)
304306
{
305-
for (var i = 0; i < data.Value.Count; i++)
307+
var listCopy = data.Value.ToList();
308+
for (var i = 0; i < listCopy.Count; i++)
306309
{
307-
data.Value[i](data.Key, dictionary[data.Key], default, ObservableUpdateType.Removed);
310+
listCopy[i](data.Key, Dictionary[data.Key], default, ObservableUpdateType.Removed);
308311
}
309312
}
310313
}
311314

312315
if (ObservableUpdateFlag != ObservableUpdateFlag.KeyUpdateOnly)
313316
{
314-
foreach (var data in dictionary)
317+
foreach (var data in Dictionary)
315318
{
316-
for (var i = 0; i < _updateActions.Count; i++)
319+
var listCopy = _updateActions.ToList();
320+
for (var i = 0; i < listCopy.Count; i++)
317321
{
318-
_updateActions[i](data.Key, data.Value, default, ObservableUpdateType.Removed);
322+
listCopy[i](data.Key, data.Value, default, ObservableUpdateType.Removed);
319323
}
320324
}
321325
}
326+
327+
Dictionary.Clear();
322328
}
323329

324330
/// <inheritdoc />
@@ -371,6 +377,7 @@ public void StopObserving(Action<TKey, TValue, TValue, ObservableUpdateType> onU
371377
if (actions.Value[i] == onUpdate)
372378
{
373379
actions.Value.RemoveAt(i);
380+
break;
374381
}
375382
}
376383
}
@@ -380,6 +387,7 @@ public void StopObserving(Action<TKey, TValue, TValue, ObservableUpdateType> onU
380387
if (_updateActions[i] == onUpdate)
381388
{
382389
_updateActions.RemoveAt(i);
390+
break;
383391
}
384392
}
385393
}

Runtime/ObservableList.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -241,17 +241,18 @@ public virtual void RemoveAt(int index)
241241
/// <inheritdoc />
242242
public virtual void Clear()
243243
{
244-
var list = new List<T>(List);
244+
// Create a copy in case that one of the callbacks modifies the list (Ex: removing a subscriber)
245+
var copy = _updateActions.ToList();
245246

246-
List.Clear();
247-
248-
for (var i = 0; i < _updateActions.Count; i++)
247+
for (var i = copy.Count - 1; i > -1; i--)
249248
{
250-
for (var j = 0; j < list.Count; j++)
249+
for (var j = 0; j < List.Count; j++)
251250
{
252-
_updateActions[i](j, list[j], default, ObservableUpdateType.Removed);
251+
copy[i](j, List[j], default, ObservableUpdateType.Removed);
253252
}
254253
}
254+
255+
List.Clear();
255256
}
256257

257258
/// <inheritdoc />

Tests/Editor/ObservableListTest.cs

+16-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ public void StopObserveCheck()
125125
_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
126126
}
127127

128+
[Test]
129+
public void StopObserve_MultipleCalls_StopsOnlyOne()
130+
{
131+
_list.Observe(_caller.Call);
132+
_list.Observe(_caller.Call);
133+
_list.StopObserving(_caller.Call);
134+
_list.Add(_previousValue);
135+
136+
_list[_index] = _previousValue;
137+
138+
_list.RemoveAt(_index);
139+
140+
_caller.Received().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
141+
}
142+
128143
[Test]
129144
public void StopObservingAllCheck()
130145
{
@@ -137,7 +152,7 @@ public void StopObservingAllCheck()
137152
}
138153

139154
[Test]
140-
public void StopObservingAll_MultipleCalls_Check()
155+
public void StopObservingAll_MultipleCalls_StopsAll()
141156
{
142157
_list.Observe(_caller.Call);
143158
_list.Observe(_caller.Call);

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "com.gamelovers.dataextensions",
33
"displayName": "Unity Data Type Extensions",
44
"author": "Miguel Tomas",
5-
"version": "0.6.4",
5+
"version": "0.6.5",
66
"unity": "2022.3",
77
"license": "MIT",
88
"description": "This package extends various sets of data types to be used in any type of data containers or persistent serializable data",

0 commit comments

Comments
 (0)