com.amanotes.prefabpool 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/Docs/PrefabPool-API.md +14 -10
- package/README.md +2 -2
- package/Runtime/MaterialPool.cs +12 -11
- package/Runtime/PrefabPool.cs +8 -8
- package/Runtime/UnityObjectIdentity.cs +60 -0
- package/Runtime/UnityObjectIdentity.cs.meta +11 -0
- package/Tests/Runtime/PrefabPoolGroupTests.cs +15 -31
- package/Tests/Runtime/PrefabPoolLifecycleTests.cs +1 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.0] - 2026-07-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `UnityObjectId` / `UnityObjectIdentity` — cross-version object identity using `EntityId` on Unity 6.0.4+ and `GetInstanceID()` on earlier versions (`UnityObjectIdentity.cs`)
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **Breaking:** `MaterialPool.Take(Material, int borrowId)` → `MaterialPool.Take(Material, UnityObject borrower)` (`MaterialPool.cs`)
|
|
10
|
+
- **Breaking:** `MaterialPool.Return(int borrowId)` → `MaterialPool.Return(UnityObject borrower)` (`MaterialPool.cs`)
|
|
11
|
+
- Internal pool dictionaries use `UnityObjectId` keys instead of `int` instance IDs (`MaterialPool.cs`, `PrefabPool.cs`)
|
|
12
|
+
|
|
3
13
|
## [1.3.0] - 2026-06-12
|
|
4
14
|
|
|
5
15
|
### Added
|
package/Docs/PrefabPool-API.md
CHANGED
|
@@ -175,22 +175,24 @@ Static utility for pooling material instances.
|
|
|
175
175
|
```csharp
|
|
176
176
|
public static class MaterialPool
|
|
177
177
|
{
|
|
178
|
-
public static Material Take(Material sourceOrClone,
|
|
179
|
-
public static void Return(
|
|
178
|
+
public static Material Take(Material sourceOrClone, UnityObject borrower);
|
|
179
|
+
public static void Return(UnityObject borrower);
|
|
180
180
|
}
|
|
181
181
|
```
|
|
182
182
|
|
|
183
183
|
**Example:**
|
|
184
184
|
```csharp
|
|
185
|
+
using UnityEngine;
|
|
186
|
+
|
|
185
187
|
// Borrow material
|
|
186
|
-
Material mat = MaterialPool.Take(originalMaterial,
|
|
187
|
-
renderer.
|
|
188
|
+
Material mat = MaterialPool.Take(originalMaterial, renderer);
|
|
189
|
+
renderer.sharedMaterial = mat;
|
|
188
190
|
mat.SetAlpha(0.5f);
|
|
189
191
|
|
|
190
192
|
// Return on destroy
|
|
191
193
|
void OnDestroy()
|
|
192
194
|
{
|
|
193
|
-
MaterialPool.Return(
|
|
195
|
+
MaterialPool.Return(renderer);
|
|
194
196
|
}
|
|
195
197
|
```
|
|
196
198
|
|
|
@@ -313,17 +315,19 @@ void UpdateTileCount(int count)
|
|
|
313
315
|
### Pattern 4: Material Pooling
|
|
314
316
|
|
|
315
317
|
```csharp
|
|
318
|
+
using UnityEngine;
|
|
319
|
+
|
|
316
320
|
// When to use: Dynamic color/alpha changes without creating material instances
|
|
317
321
|
public class ColoredTile : MonoBehaviour
|
|
318
322
|
{
|
|
319
323
|
private Material instanceMaterial;
|
|
320
|
-
private
|
|
324
|
+
private Renderer _renderer;
|
|
321
325
|
|
|
322
326
|
void Start()
|
|
323
327
|
{
|
|
324
|
-
|
|
325
|
-
instanceMaterial = MaterialPool.Take(
|
|
326
|
-
|
|
328
|
+
_renderer = GetComponent<Renderer>();
|
|
329
|
+
instanceMaterial = MaterialPool.Take(_renderer.sharedMaterial, _renderer);
|
|
330
|
+
_renderer.sharedMaterial = instanceMaterial;
|
|
327
331
|
}
|
|
328
332
|
|
|
329
333
|
public void SetColor(Color color)
|
|
@@ -333,7 +337,7 @@ public class ColoredTile : MonoBehaviour
|
|
|
333
337
|
|
|
334
338
|
void OnDestroy()
|
|
335
339
|
{
|
|
336
|
-
MaterialPool.Return(
|
|
340
|
+
MaterialPool.Return(_renderer);
|
|
337
341
|
}
|
|
338
342
|
}
|
|
339
343
|
```
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# PrefabPool
|
|
2
2
|
|
|
3
3
|
**Package:** `com.amanotes.prefabpool`
|
|
4
|
-
**Version:**
|
|
4
|
+
**Version:** 2.0.0
|
|
5
5
|
|
|
6
6
|
Generic object pooling system for Unity with lifecycle callbacks and material pooling support.
|
|
7
7
|
|
|
@@ -30,7 +30,7 @@ Add the package to your Unity project via `Packages/manifest.json`:
|
|
|
30
30
|
```json
|
|
31
31
|
{
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"com.amanotes.prefabpool": "
|
|
33
|
+
"com.amanotes.prefabpool": "2.0.0"
|
|
34
34
|
},
|
|
35
35
|
"scopedRegistries": [
|
|
36
36
|
{
|
package/Runtime/MaterialPool.cs
CHANGED
|
@@ -23,28 +23,29 @@ namespace Amanotes.Core
|
|
|
23
23
|
|
|
24
24
|
public partial class MaterialPool
|
|
25
25
|
{
|
|
26
|
-
[NonSerialized] private static readonly Dictionary<
|
|
27
|
-
[NonSerialized] private static readonly Dictionary<
|
|
26
|
+
[NonSerialized] private static readonly Dictionary<UnityObjectId, MaterialPool> _map = new Dictionary<UnityObjectId, MaterialPool>();
|
|
27
|
+
[NonSerialized] private static readonly Dictionary<UnityObjectId, MaterialPool> _cloned = new Dictionary<UnityObjectId, MaterialPool>();
|
|
28
28
|
|
|
29
29
|
private static MaterialPool GetPool(Material sourceOrClone)
|
|
30
30
|
{
|
|
31
31
|
if (sourceOrClone == null) return null;
|
|
32
|
-
var instId =
|
|
32
|
+
var instId = UnityObjectIdentity.GetId(sourceOrClone);
|
|
33
33
|
var isCloned = sourceOrClone.hideFlags == HideFlags.DontSave;
|
|
34
34
|
return isCloned
|
|
35
35
|
? _cloned.GetValueOrDefault(instId)
|
|
36
36
|
: _map.GetValueOrDefault(instId);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
public static Material Take(Material sourceOrClone,
|
|
39
|
+
public static Material Take(Material sourceOrClone, UnityObject borrower)
|
|
40
40
|
{
|
|
41
41
|
if (sourceOrClone == null) return null;
|
|
42
42
|
var pool = GetPool(sourceOrClone) ?? new MaterialPool(sourceOrClone);
|
|
43
|
-
return pool.InternalTake(
|
|
43
|
+
return pool.InternalTake(UnityObjectIdentity.GetId(borrower));
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
public static void Return(
|
|
46
|
+
public static void Return(UnityObject borrower)
|
|
47
47
|
{
|
|
48
|
+
var borrowId = UnityObjectIdentity.GetId(borrower);
|
|
48
49
|
foreach (var kvp in _map)
|
|
49
50
|
{
|
|
50
51
|
kvp.Value.InternalReturn(borrowId);
|
|
@@ -56,12 +57,12 @@ namespace Amanotes.Core
|
|
|
56
57
|
{
|
|
57
58
|
private readonly Material original;
|
|
58
59
|
private readonly List<Material> pool = new List<Material>();
|
|
59
|
-
private readonly Dictionary<
|
|
60
|
+
private readonly Dictionary<UnityObjectId, Material> borrowed = new Dictionary<UnityObjectId, Material>();
|
|
60
61
|
|
|
61
62
|
private MaterialPool(Material source)
|
|
62
63
|
{
|
|
63
64
|
original = source;
|
|
64
|
-
_map[
|
|
65
|
+
_map[UnityObjectIdentity.GetId(original)] = this;
|
|
65
66
|
|
|
66
67
|
if (source.hideFlags == HideFlags.DontSave)
|
|
67
68
|
{
|
|
@@ -78,7 +79,7 @@ namespace Amanotes.Core
|
|
|
78
79
|
if (pool.Count <= 0)
|
|
79
80
|
{
|
|
80
81
|
result = new Material(original) { hideFlags = HideFlags.DontSave };
|
|
81
|
-
_cloned[
|
|
82
|
+
_cloned[UnityObjectIdentity.GetId(result)] = this;
|
|
82
83
|
return result;
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -87,7 +88,7 @@ namespace Amanotes.Core
|
|
|
87
88
|
return result;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
private Material InternalTake(
|
|
91
|
+
private Material InternalTake(UnityObjectId borrowId)
|
|
91
92
|
{
|
|
92
93
|
if (borrowed.TryGetValue(borrowId, out var mat)) return mat;
|
|
93
94
|
|
|
@@ -97,7 +98,7 @@ namespace Amanotes.Core
|
|
|
97
98
|
return material;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
private void InternalReturn(
|
|
101
|
+
private void InternalReturn(UnityObjectId borrowId)
|
|
101
102
|
{
|
|
102
103
|
if (borrowed.Remove(borrowId, out var mat)) pool.Add(mat);
|
|
103
104
|
}
|
package/Runtime/PrefabPool.cs
CHANGED
|
@@ -23,14 +23,14 @@ namespace Amanotes.Core
|
|
|
23
23
|
[NonSerialized] private PoolGroup _parentGroup;
|
|
24
24
|
|
|
25
25
|
// Per-instance IPoolPrefab cache — components don't change on pooled prefabs
|
|
26
|
-
[NonSerialized] private readonly Dictionary<
|
|
26
|
+
[NonSerialized] private readonly Dictionary<UnityObjectId, IPoolPrefab[]> _poolPrefabCache = new Dictionary<UnityObjectId, IPoolPrefab[]>(32);
|
|
27
27
|
[NonSerialized] private static readonly List<IPoolPrefab> _tempPoolPrefabs = new List<IPoolPrefab>(8);
|
|
28
28
|
|
|
29
29
|
public void SetParentGroup(PoolGroup parentGroup) => _parentGroup = parentGroup;
|
|
30
30
|
|
|
31
31
|
private IPoolPrefab[] GetCachedPoolPrefabs(GameObject go)
|
|
32
32
|
{
|
|
33
|
-
var instanceId =
|
|
33
|
+
var instanceId = UnityObjectIdentity.GetId(go);
|
|
34
34
|
if (_poolPrefabCache.TryGetValue(instanceId, out var cached)) return cached;
|
|
35
35
|
|
|
36
36
|
go.GetComponents(_tempPoolPrefabs);
|
|
@@ -311,7 +311,7 @@ namespace Amanotes.Core
|
|
|
311
311
|
[NonSerialized] private bool _isReady = false;
|
|
312
312
|
[NonSerialized] private bool _isInitializing = false;
|
|
313
313
|
[NonSerialized] private readonly Dictionary<string, PoolGroup> _groups = new Dictionary<string, PoolGroup>();
|
|
314
|
-
[NonSerialized] private readonly Dictionary<
|
|
314
|
+
[NonSerialized] private readonly Dictionary<UnityObjectId, PoolEntry> _instanceTypeMap = new Dictionary<UnityObjectId, PoolEntry>();
|
|
315
315
|
[NonSerialized] private readonly Dictionary<GameObject, PoolEntry> _customPool = new Dictionary<GameObject, PoolEntry>();
|
|
316
316
|
|
|
317
317
|
public void Resize<T>(GameObject prefab, List<T> list, int count, Transform parent) where T: Component
|
|
@@ -385,7 +385,7 @@ namespace Amanotes.Core
|
|
|
385
385
|
var result = entry.Get(transform, p, localPosition);
|
|
386
386
|
if (result != null)
|
|
387
387
|
{
|
|
388
|
-
_instanceTypeMap[
|
|
388
|
+
_instanceTypeMap[UnityObjectIdentity.GetId(result)] = entry;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
return result;
|
|
@@ -395,7 +395,7 @@ namespace Amanotes.Core
|
|
|
395
395
|
{
|
|
396
396
|
if (_isDestroyed || group == null || go == null) return;
|
|
397
397
|
|
|
398
|
-
var instanceId =
|
|
398
|
+
var instanceId = UnityObjectIdentity.GetId(go);
|
|
399
399
|
if (_instanceTypeMap.TryGetValue(instanceId, out var entry))
|
|
400
400
|
{
|
|
401
401
|
entry.Return(go, transform);
|
|
@@ -407,10 +407,10 @@ namespace Amanotes.Core
|
|
|
407
407
|
{
|
|
408
408
|
#if UNITY_EDITOR
|
|
409
409
|
if (_instanceTypeMap.Count == 0) return;
|
|
410
|
-
var stale = new List<
|
|
410
|
+
var stale = new List<UnityObjectId>();
|
|
411
411
|
foreach (var kvp in _instanceTypeMap)
|
|
412
412
|
{
|
|
413
|
-
if (
|
|
413
|
+
if (UnityObjectIdentity.IdToObject(kvp.Key) == null) stale.Add(kvp.Key);
|
|
414
414
|
}
|
|
415
415
|
foreach (var id in stale) _instanceTypeMap.Remove(id);
|
|
416
416
|
#endif
|
|
@@ -482,7 +482,7 @@ namespace Amanotes.Core
|
|
|
482
482
|
#if UNITY_EDITOR
|
|
483
483
|
foreach (var kvp in _instanceTypeMap)
|
|
484
484
|
{
|
|
485
|
-
var obj =
|
|
485
|
+
var obj = UnityObjectIdentity.IdToObject(kvp.Key);
|
|
486
486
|
if (obj != null) UnityObject.DestroyImmediate(obj);
|
|
487
487
|
}
|
|
488
488
|
#endif
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using UnityEngine;
|
|
3
|
+
using UnityObject = UnityEngine.Object;
|
|
4
|
+
|
|
5
|
+
namespace Amanotes.Core
|
|
6
|
+
{
|
|
7
|
+
internal readonly struct UnityObjectId : IEquatable<UnityObjectId>
|
|
8
|
+
{
|
|
9
|
+
#if UNITY_6000_4_OR_NEWER
|
|
10
|
+
private readonly EntityId _id;
|
|
11
|
+
|
|
12
|
+
public UnityObjectId(UnityObject obj) => _id = obj != null ? obj.GetEntityId() : default;
|
|
13
|
+
|
|
14
|
+
public bool IsValid => _id.IsValid();
|
|
15
|
+
|
|
16
|
+
public bool Equals(UnityObjectId other) => _id.Equals(other._id);
|
|
17
|
+
|
|
18
|
+
public override int GetHashCode() => _id.GetHashCode();
|
|
19
|
+
|
|
20
|
+
public override string ToString() => _id.ToString();
|
|
21
|
+
|
|
22
|
+
#if UNITY_EDITOR
|
|
23
|
+
public UnityObject ToObject() => UnityEditor.EditorUtility.EntityIdToObject(_id);
|
|
24
|
+
#endif
|
|
25
|
+
#else
|
|
26
|
+
private readonly int _id;
|
|
27
|
+
|
|
28
|
+
public UnityObjectId(UnityObject obj) => _id = obj != null ? obj.GetInstanceID() : 0;
|
|
29
|
+
|
|
30
|
+
public bool IsValid => _id != 0;
|
|
31
|
+
|
|
32
|
+
public bool Equals(UnityObjectId other) => _id == other._id;
|
|
33
|
+
|
|
34
|
+
public override int GetHashCode() => _id;
|
|
35
|
+
|
|
36
|
+
public override string ToString() => _id.ToString();
|
|
37
|
+
|
|
38
|
+
#if UNITY_EDITOR
|
|
39
|
+
public UnityObject ToObject() => UnityEditor.EditorUtility.InstanceIDToObject(_id);
|
|
40
|
+
#endif
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
public static bool operator ==(UnityObjectId left, UnityObjectId right) => left.Equals(right);
|
|
44
|
+
|
|
45
|
+
public static bool operator !=(UnityObjectId left, UnityObjectId right) => !left.Equals(right);
|
|
46
|
+
|
|
47
|
+
public override bool Equals(object obj) => obj is UnityObjectId other && Equals(other);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
internal static class UnityObjectIdentity
|
|
51
|
+
{
|
|
52
|
+
public static UnityObjectId GetId(UnityObject obj) => new UnityObjectId(obj);
|
|
53
|
+
|
|
54
|
+
public static bool IsValid(UnityObjectId id) => id.IsValid;
|
|
55
|
+
|
|
56
|
+
#if UNITY_EDITOR
|
|
57
|
+
public static UnityObject IdToObject(UnityObjectId id) => id.ToObject();
|
|
58
|
+
#endif
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -119,11 +119,8 @@ namespace Amanotes.Core
|
|
|
119
119
|
var objFromB = _groupB.Get("PrefabB");
|
|
120
120
|
Assert.IsNotNull(objFromB, $"Iteration {i}: GroupB should return valid GameObject");
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
Assert.AreNotEqual(instanceIdA, instanceIdB,
|
|
126
|
-
$"Iteration {i}: Objects from different groups should have different instance IDs");
|
|
122
|
+
Assert.AreNotSame(objFromA, objFromB,
|
|
123
|
+
$"Iteration {i}: Objects from different groups should be different instances");
|
|
127
124
|
|
|
128
125
|
Assert.IsTrue(objFromA.name.Contains("PrefabA"),
|
|
129
126
|
$"Iteration {i}: Object from GroupA should be PrefabA instance");
|
|
@@ -136,9 +133,9 @@ namespace Amanotes.Core
|
|
|
136
133
|
var reusedFromA = _groupA.Get("PrefabA");
|
|
137
134
|
var reusedFromB = _groupB.Get("PrefabB");
|
|
138
135
|
|
|
139
|
-
Assert.
|
|
136
|
+
Assert.AreSame(objFromA, reusedFromA,
|
|
140
137
|
$"Iteration {i}: GroupA should reuse its own objects");
|
|
141
|
-
Assert.
|
|
138
|
+
Assert.AreSame(objFromB, reusedFromB,
|
|
142
139
|
$"Iteration {i}: GroupB should reuse its own objects");
|
|
143
140
|
|
|
144
141
|
Assert.IsTrue(reusedFromA.name.Contains("PrefabA"),
|
|
@@ -168,11 +165,6 @@ namespace Amanotes.Core
|
|
|
168
165
|
Assert.IsNotNull(objB1, $"Iteration {i}: GroupB Get 1 should return valid GameObject");
|
|
169
166
|
Assert.IsNotNull(objB2, $"Iteration {i}: GroupB Get 2 should return valid GameObject");
|
|
170
167
|
|
|
171
|
-
var instanceIdA1 = objA1.GetInstanceID();
|
|
172
|
-
var instanceIdA2 = objA2.GetInstanceID();
|
|
173
|
-
var instanceIdB1 = objB1.GetInstanceID();
|
|
174
|
-
var instanceIdB2 = objB2.GetInstanceID();
|
|
175
|
-
|
|
176
168
|
_groupA.Return(objA1);
|
|
177
169
|
_groupA.Return(objA2);
|
|
178
170
|
_groupB.Return(objB1);
|
|
@@ -183,35 +175,27 @@ namespace Amanotes.Core
|
|
|
183
175
|
var reusedB1 = _groupB.Get("PrefabB");
|
|
184
176
|
var reusedB2 = _groupB.Get("PrefabB");
|
|
185
177
|
|
|
186
|
-
var
|
|
187
|
-
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
};
|
|
191
|
-
var reusedInstanceIdsB = new HashSet<int>
|
|
192
|
-
{
|
|
193
|
-
reusedB1.GetInstanceID(),
|
|
194
|
-
reusedB2.GetInstanceID()
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
Assert.IsTrue(reusedInstanceIdsA.Contains(instanceIdA1),
|
|
178
|
+
var reusedObjectsA = new HashSet<GameObject> { reusedA1, reusedA2 };
|
|
179
|
+
var reusedObjectsB = new HashSet<GameObject> { reusedB1, reusedB2 };
|
|
180
|
+
|
|
181
|
+
Assert.IsTrue(reusedObjectsA.Contains(objA1),
|
|
198
182
|
$"Iteration {i}: Object returned to GroupA should be retrieved from GroupA");
|
|
199
|
-
Assert.IsTrue(
|
|
183
|
+
Assert.IsTrue(reusedObjectsA.Contains(objA2),
|
|
200
184
|
$"Iteration {i}: Object returned to GroupA should be retrieved from GroupA");
|
|
201
185
|
|
|
202
|
-
Assert.IsTrue(
|
|
186
|
+
Assert.IsTrue(reusedObjectsB.Contains(objB1),
|
|
203
187
|
$"Iteration {i}: Object returned to GroupB should be retrieved from GroupB");
|
|
204
|
-
Assert.IsTrue(
|
|
188
|
+
Assert.IsTrue(reusedObjectsB.Contains(objB2),
|
|
205
189
|
$"Iteration {i}: Object returned to GroupB should be retrieved from GroupB");
|
|
206
190
|
|
|
207
|
-
Assert.IsFalse(
|
|
191
|
+
Assert.IsFalse(reusedObjectsA.Contains(objB1),
|
|
208
192
|
$"Iteration {i}: GroupA should not return objects from GroupB");
|
|
209
|
-
Assert.IsFalse(
|
|
193
|
+
Assert.IsFalse(reusedObjectsA.Contains(objB2),
|
|
210
194
|
$"Iteration {i}: GroupA should not return objects from GroupB");
|
|
211
195
|
|
|
212
|
-
Assert.IsFalse(
|
|
196
|
+
Assert.IsFalse(reusedObjectsB.Contains(objA1),
|
|
213
197
|
$"Iteration {i}: GroupB should not return objects from GroupA");
|
|
214
|
-
Assert.IsFalse(
|
|
198
|
+
Assert.IsFalse(reusedObjectsB.Contains(objA2),
|
|
215
199
|
$"Iteration {i}: GroupB should not return objects from GroupA");
|
|
216
200
|
|
|
217
201
|
_groupA.Return(reusedA1);
|
|
@@ -81,16 +81,12 @@ namespace Amanotes.Core
|
|
|
81
81
|
var firstGet = _testGroup.Get("TestPrefab");
|
|
82
82
|
Assert.IsNotNull(firstGet, $"Iteration {i}: First Get should return valid GameObject");
|
|
83
83
|
|
|
84
|
-
var firstInstanceId = firstGet.GetInstanceID();
|
|
85
|
-
|
|
86
84
|
_testGroup.Return(firstGet);
|
|
87
85
|
|
|
88
86
|
var secondGet = _testGroup.Get("TestPrefab");
|
|
89
87
|
Assert.IsNotNull(secondGet, $"Iteration {i}: Second Get should return valid GameObject");
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Assert.AreEqual(firstInstanceId, secondInstanceId,
|
|
89
|
+
Assert.AreSame(firstGet, secondGet,
|
|
94
90
|
$"Iteration {i}: Get-Return-Get should reuse same instance (validates pooling eliminates instantiation)");
|
|
95
91
|
|
|
96
92
|
_testGroup.Return(secondGet);
|