Skip to content

Commit c9b7e37

Browse files
Checkpoint.
1 parent 48554cd commit c9b7e37

16 files changed

Lines changed: 199 additions & 121 deletions

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,6 @@ dotnet_naming_style.begins_with_i.required_prefix = I
219219
dotnet_naming_style.begins_with_i.required_suffix =
220220
dotnet_naming_style.begins_with_i.word_separator =
221221
dotnet_naming_style.begins_with_i.capitalization = pascal_case
222+
223+
# IDE0057: Use range operator
224+
dotnet_diagnostic.IDE0057.severity = silent

source/CollectionWrapper.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@
33

44
namespace Open.Collections;
55

6-
public abstract class CollectionWrapper<T, TCollection> : ReadOnlyCollectionWrapper<T, TCollection>, ICollection<T>
6+
public class CollectionWrapper<T, TCollection> : ReadOnlyCollectionWrapper<T, TCollection>, ICollection<T>
77
where TCollection : class, ICollection<T>
88
{
9-
protected CollectionWrapper(TCollection source) : base(source)
9+
public CollectionWrapper(TCollection source, bool owner = false)
10+
: base(source, owner)
1011
{
1112
}
1213

14+
protected readonly object Sync = new(); // Could possibly override..
15+
16+
/// <summary>
17+
/// The underlying object used for synchronization.
18+
/// This is exposed to allow for more complex synchronization operations.
19+
/// </summary>
20+
public object SyncRoot => Sync;
21+
1322
#region Implementation of ICollection<T>
1423

1524
[MethodImpl(MethodImplOptions.AggressiveInlining)]

source/DictionaryToHashSetWrapper.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ public class DictionaryToHashSetWrapper<T> : ISet<T>
99
protected readonly IDictionary<T, bool> InternalSource;
1010

1111
// ReSharper disable once MemberCanBeProtected.Global
12-
public DictionaryToHashSetWrapper(IDictionary<T, bool> source) => InternalSource = source;
12+
public DictionaryToHashSetWrapper(IDictionary<T, bool> source)
13+
=> InternalSource = source;
1314

1415
/// <inheritdoc />
1516
public int Count

source/DictionaryWrapper.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ public class DictionaryWrapper<TKey, TValue>
99
{
1010
/// <inheritdoc />
1111
public DictionaryWrapper(int capacity = 0)
12-
: base(new Dictionary<TKey, TValue>(capacity))
12+
: base(new Dictionary<TKey, TValue>(capacity), true)
13+
{
14+
}
15+
16+
public DictionaryWrapper(IDictionary<TKey, TValue> source, bool owned = false)
17+
: base(source, owned)
1318
{
1419
}
1520

source/GlobalSuppressions.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

source/ListWrapper.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
using System.Runtime.CompilerServices;
33

44
namespace Open.Collections;
5-
public abstract class ListWrapper<T> : CollectionWrapper<T, IList<T>>, IList<T>
5+
public class ListWrapper<T> : CollectionWrapper<T, IList<T>>, IList<T>
66
{
7-
protected ListWrapper(IList<T> source) : base(source)
7+
public ListWrapper(int capacity = 0)
8+
: base(new List<T>(capacity), true)
9+
{
10+
}
11+
12+
public ListWrapper(IList<T> source, bool owner = false)
13+
: base(source, owner)
814
{
915
}
1016

source/ReadOnlyCollectionWrapper.cs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,22 @@ public class ReadOnlyCollectionWrapper<T, TCollection> : DisposableBase, IReadOn
1010
where TCollection : class, ICollection<T>
1111
{
1212
protected TCollection InternalSource;
13+
protected readonly bool SourceOwned;
1314

14-
protected ReadOnlyCollectionWrapper(TCollection source)
15-
=> InternalSource = source ?? throw new ArgumentNullException(nameof(source));
15+
/// <summary>
16+
/// Constructs a wrapper for read-only access to a collection.
17+
/// </summary>
18+
/// <param name="source">The source collection.</param>
19+
/// <param name="owner">
20+
/// If <see langword="true"/>, will call <paramref name="source"/>.Dispose() if the source is <see cref="IDisposable"/> when this is disposed.<br/>
21+
/// And will throw <see cref="NotSupportedException"/> to prevent direct access to the source when .ExtractAndDispose() is called.
22+
/// </param>
23+
/// <exception cref="ArgumentNullException">If the <paramref name="source"/> is <see langword="null"/>.</exception>
24+
public ReadOnlyCollectionWrapper(TCollection source, bool owner = false)
25+
{
26+
InternalSource = source ?? throw new ArgumentNullException(nameof(source));
27+
SourceOwned = owner;
28+
}
1629

1730
#region Implementation of IReadOnlyCollection<T>
1831
/// <inheritdoc cref="ICollection&lt;T&gt;.Contains(T)" />
@@ -52,15 +65,21 @@ public virtual Span<T> CopyTo(Span<T> span)
5265
public virtual void Export(ICollection<T> to)
5366
=> to.Add(InternalSource);
5467

55-
#region Dispose
56-
protected override void OnDispose() => Nullify(ref InternalSource);
68+
#region Dispose
69+
protected override void OnDispose()
70+
{
71+
var source = Nullify(ref InternalSource);
72+
if (SourceOwned && source is IDisposable d) d.Dispose();
73+
}
5774

58-
/// <summary>
59-
/// Extracts the underlying collection and returns it before disposing of this synchronized wrapper.
60-
/// </summary>
61-
/// <returns>The extracted underlying collection.</returns>
62-
public TCollection ExtractAndDispose()
75+
/// <summary>
76+
/// Extracts the underlying collection and returns it before disposing of this synchronized wrapper.
77+
/// </summary>
78+
/// <returns>The extracted underlying collection.</returns>
79+
/// <exception cref="NotSupportedException">If the underlying collection is owned by this wrapper.</exception>
80+
public TCollection ExtractAndDispose()
6381
{
82+
if (SourceOwned) throw new NotSupportedException("The underlying collection is owned by this wrapper.");
6483
using (this)
6584
{
6685
return Nullify(ref InternalSource);

source/SimpleOrderedDictionary.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public class SimpleOrderedDictionary<TKey, TValue>
1313
private readonly List<TKey> _keys;
1414
private readonly List<TValue> _values;
1515

16-
public object SyncRoot { get; } = new object();
17-
1816
public SimpleOrderedDictionary(int capacity = 0)
1917
: base(capacity)
2018
{

source/Synchronized/ConcurrentList.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,26 @@ public class ConcurrentList<T> : ListWrapper<T>, ISynchronizedCollection<T>
1919
public override int Count => _count;
2020
private readonly ConcurrentQueue<T> _buffer = new();
2121
private readonly ReaderWriterLockSlim _sync = new();
22-
private ReaderWriterLockSlim Sync
22+
private ReaderWriterLockSlim RWLock
2323
=> AssertIsAlive() ? _sync : null!;
2424

2525
protected override void OnDispose()
2626
{
2727
Clear();
28-
Sync.Dispose();
28+
RWLock.Dispose();
2929
base.OnDispose();
3030
}
3131

3232
private void DumpBuffer()
3333
{
3434
if (_buffer.IsEmpty) return;
35-
using var write = Sync.WriteLock();
35+
using var write = RWLock.WriteLock();
3636
DumpBufferUnlocked();
3737
}
3838

3939
private void DumpBufferUnlocked()
4040
{
41-
Debug.Assert(Sync.IsWriteLockHeld);
41+
Debug.Assert(RWLock.IsWriteLockHeld);
4242
while (_buffer.TryDequeue(out var item))
4343
InternalSource.Add(item);
4444
}
@@ -77,18 +77,18 @@ protected override void AddInternal(T item)
7777
/// <inheritdoc />
7878
public override int IndexOf(T item)
7979
{
80-
int i = Sync.Read(() => base.IndexOf(item));
80+
int i = RWLock.Read(() => base.IndexOf(item));
8181
if (i != -1 || _buffer.IsEmpty) return i;
8282
DumpBuffer(); // one dump then accept results.
83-
return Sync.Read(() => base.IndexOf(item));
83+
return RWLock.Read(() => base.IndexOf(item));
8484
}
8585

8686
/// <inheritdoc />
8787
public override void Insert(int index, T item)
8888
{
8989
AssertValidIndex(index);
9090
DumpBuffer();
91-
using var write = Sync.WriteLock();
91+
using var write = RWLock.WriteLock();
9292
base.Insert(index, item);
9393
Interlocked.Increment(ref _count);
9494
}
@@ -103,15 +103,15 @@ public override void RemoveAt(int index)
103103

104104
private void RemoveAtCore(int index)
105105
{
106-
using var write = Sync.WriteLock();
106+
using var write = RWLock.WriteLock();
107107
base.RemoveAt(index);
108108
Interlocked.Decrement(ref _count);
109109
}
110110

111111
/// <inheritdoc />
112112
public override bool Remove(T item)
113113
// Assume the majority case is that the item exists.
114-
=> Sync.ReadUpgradeable(() =>
114+
=> RWLock.ReadUpgradeable(() =>
115115
{
116116
DumpBuffer();
117117
int i = base.IndexOf(item);
@@ -122,7 +122,7 @@ public override bool Remove(T item)
122122

123123
/// <inheritdoc />
124124
public override void Clear()
125-
=> Sync.Write(() =>
125+
=> RWLock.Write(() =>
126126
{
127127
DumpBufferUnlocked();
128128
int i = InternalSource.Count;
@@ -138,7 +138,7 @@ public override bool Contains(T item)
138138
public override void CopyTo(T[] array, int arrayIndex)
139139
{
140140
DumpBuffer();
141-
Sync.Read(() => base.CopyTo(array, arrayIndex));
141+
RWLock.Read(() => base.CopyTo(array, arrayIndex));
142142
}
143143

144144
/// <inheritdoc />
@@ -152,6 +152,6 @@ public override IEnumerator<T> GetEnumerator()
152152
public T[] Snapshot()
153153
{
154154
DumpBuffer();
155-
return Sync.Read(()=>InternalSource.ToArray());
155+
return RWLock.Read(()=>InternalSource.ToArray());
156156
}
157157
}

source/Synchronized/LockSynchronizedCollectionWrapper.cs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,8 @@ namespace Open.Collections.Synchronized;
88
public class LockSynchronizedCollectionWrapper<T, TCollection> : CollectionWrapper<T, TCollection>, ISynchronizedCollectionWrapper<T, TCollection>
99
where TCollection : class, ICollection<T>
1010
{
11-
protected LockSynchronizedCollectionWrapper(TCollection source) : base(source) => Sync = source;
12-
13-
protected readonly object Sync; // Could possibly override..
14-
15-
/// <summary>
16-
/// The underlying object used for synchronization. This is exposed to allow for more complex synchronization operations.
17-
/// </summary>
18-
public object SyncRoot => Sync;
11+
protected LockSynchronizedCollectionWrapper(TCollection source)
12+
: base(source) { }
1913

2014
#region Implementation of ICollection<T>
2115

@@ -156,9 +150,9 @@ public virtual bool IfContains(T item, Action<TCollection> action)
156150
{
157151
lock (Sync)
158152
{
159-
bool contains = InternalSource.Contains(item);
160-
if (contains) action(InternalSource);
161-
return contains;
153+
if (!InternalSource.Contains(item)) return false;
154+
action(InternalSource);
155+
return true;
162156
}
163157
}
164158

@@ -167,9 +161,9 @@ public virtual bool IfNotContains(T item, Action<TCollection> action)
167161
{
168162
lock (Sync)
169163
{
170-
bool notContains = !InternalSource.Contains(item);
171-
if (notContains) action(InternalSource);
172-
return notContains;
173-
}
174-
}
164+
if (InternalSource.Contains(item)) return false;
165+
action(InternalSource);
166+
return true;
167+
}
168+
}
175169
}

0 commit comments

Comments
 (0)