Skip to content

Commit ab825ed

Browse files
Revisions.
1 parent 5ceb00b commit ab825ed

15 files changed

Lines changed: 551 additions & 78 deletions

source/CollectionWrapper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ protected virtual void AddInternal(T item)
3232
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3333
public virtual void Add(T item) => AddInternal(item);
3434

35-
/// <inheritdoc cref="IAddMultiple{T}.Add(T, T, T[])"/>
36-
public virtual void Add(T item1, T item2, params T[] items)
35+
/// <inheritdoc cref="IAddMultiple{T}.AddThese(T, T, T[])"/>
36+
public virtual void AddThese(T item1, T item2, params T[] items)
3737
{
3838
AddInternal(item1);
3939
AddInternal(item2);

source/IAddMultiple.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Open.Collections;
44
public interface IAddMultiple<T>
55
{
6+
// Note: "AddThese" is the name because Add can have multiple signatures.
7+
68
/// <summary>Adds more than one item.</summary>
79
/// <param name="item1">First item to add.</param>
810
/// <param name="item2">Additional item to add.</param>

source/IOrderedDictionary.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ public interface IOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
3232
/// The <see cref="IOrderedDictionary{TKey,TValue}"/> has a fized size.</exception>
3333
void Insert(int index, TKey key, TValue value);
3434

35+
/// <summary>
36+
/// Updates or creates and item.
37+
/// </summary>
38+
/// <returns><see langword="true"/> if the value was updated; otherwise <see langword="false"/>.</returns>
39+
bool SetValue(TKey key, TValue value, out int index);
40+
3541
/// <summary>
3642
/// Updates or creates and item.
3743
/// </summary>
@@ -55,7 +61,7 @@ public interface IOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
5561
/// </summary>
5662
/// <remarks><see langword="true"/> if the value changed; otherwise <see langword="false"/>.</remarks>
5763
/// <exception cref="ArgumentOutOfRangeException">The index is less than zero or greater than the length of the collection.</exception>
58-
bool SetValueAt(int index, TValue value);
64+
bool SetValueAt(int index, TValue value, out TKey key);
5965

6066
/// <summary>
6167
/// Removes the entry at the specified index from the <see cref="IOrderedDictionary{TKey,TValue}"/> items.

source/ItemChangedEventArgs.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,34 @@ public enum ItemChange
1414
Modified
1515
}
1616

17-
public class ItemChangedEventArgs<TIValue> : EventArgs
17+
public class ItemChangedEventArgs<T> : EventArgs
1818
{
1919
public readonly ItemChange Change;
20-
public readonly TIValue Value;
21-
public readonly TIValue? PreviousValue;
20+
public readonly T Value;
21+
public readonly int Version;
2222

23-
public ItemChangedEventArgs(ItemChange action, TIValue value)
23+
public ItemChangedEventArgs(ItemChange action, T value, int version)
2424
{
2525
Change = action;
2626
Value = value;
27-
}
28-
29-
public ItemChangedEventArgs(ItemChange action, TIValue previous, TIValue value) : this(action, value) => PreviousValue = previous;
27+
Version = version;
28+
}
3029
}
3130

32-
public class KeyValueChangedEventArgs<TIKey, TIValue> : ItemChangedEventArgs<TIValue>
31+
public class ItemChangedEventArgs<TIndex, TValue> : ItemChangedEventArgs<TValue>
3332
{
34-
public readonly TIKey Key;
35-
36-
public KeyValueChangedEventArgs(ItemChange action, TIKey key, TIValue value) : base(action, value) => Key = key;
37-
38-
public KeyValueChangedEventArgs(ItemChange action, TIKey key, TIValue previous, TIValue value) : base(action, previous, value) => Key = key;
33+
public readonly TIndex Index;
34+
public ItemChangedEventArgs(ItemChange action, TIndex index, TValue value, int version)
35+
: base(action, value, version) => Index = index;
3936
}
4037

41-
public delegate void KeyValueChangedEventHandler<TIKey, TIValue>(object source, KeyValueChangedEventArgs<TIKey, TIValue> e);
38+
public static class ItemChangeEventArgs
39+
{
40+
public static ItemChangedEventArgs<T> CreateArgs<T>(
41+
this ItemChange change, T value, int version)
42+
=> new(change, value, version);
43+
44+
public static ItemChangedEventArgs<TIndex, TValue> CreateArgs<TIndex, TValue>(
45+
this ItemChange change, TIndex index, TValue value, int version)
46+
=> new(change, index, value, version);
47+
}

source/Open.Collections.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
<ItemGroup>
4747
<PackageReference Include="Open.Text" Version="6.2.0" />
48-
<PackageReference Include="Open.Threading" Version="2.0.0" />
48+
<PackageReference Include="Open.Threading" Version="2.1.1" />
4949
<PackageReference Include="System.Collections.Immutable" Version="6.0.0" />
5050
</ItemGroup>
5151

source/OrderedDictionary.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,25 @@ public int SetValue(TKey key, TValue value)
153153
}
154154

155155
/// <inheritdoc />
156-
public bool SetValueAt(int index, TValue value)
156+
public bool SetValue(TKey key, TValue value, out int index)
157157
{
158-
var key = _keys[index];
158+
if (!InternalSource.ContainsKey(key))
159+
{
160+
index = Add(key, value);
161+
return true;
162+
}
163+
164+
index = GetIndex(key);
165+
InternalSource[key] = value;
166+
var previous = _values[index];
167+
_values[index] = value;
168+
return !(previous?.Equals(value) ?? value is null);
169+
}
170+
171+
/// <inheritdoc />
172+
public bool SetValueAt(int index, TValue value, out TKey key)
173+
{
174+
key = _keys[index];
159175
if (InternalSource.TryGetValue(key, out var previous))
160176
{
161177
if (previous is null)

source/Synchronized/LockSynchronizedCollectionWrapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public override void Add(T item)
2323

2424
/// <inheritdoc />
2525
[SuppressMessage("Roslynator", "RCS1235:Optimize method call.")]
26-
public override void Add(T item1, T item2, params T[] items)
26+
public override void AddThese(T item1, T item2, params T[] items)
2727
{
2828
lock (Sync)
2929
{

source/Synchronized/LockSynchronizedOrderedDictionary.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ public void RemoveAt(int index)
2929
lock (Sync) InternalSource.RemoveAt(index);
3030
}
3131

32+
/// <inheritdoc />
33+
public bool SetValue(TKey key, TValue value, out int index)
34+
{
35+
lock (Sync) return InternalSource.SetValue(key, value, out index);
36+
}
37+
3238
/// <inheritdoc />
3339
public int SetValue(TKey key, TValue value)
3440
{
3541
lock (Sync) return InternalSource.SetValue(key, value);
3642
}
3743

3844
/// <inheritdoc />
39-
public bool SetValueAt(int index, TValue value)
45+
public bool SetValueAt(int index, TValue value, out TKey key)
4046
{
41-
lock (Sync) return InternalSource.SetValueAt(index, value);
47+
lock (Sync) return InternalSource.SetValueAt(index, value, out key);
4248
}
4349

4450
/// <inheritdoc />

source/Synchronized/ReadWriteSynchronizedCollectionWrapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public override void Add(T item)
3232

3333
/// <inheritdoc />
3434
[SuppressMessage("Roslynator", "RCS1235:Optimize method call.")]
35-
public override void Add(T item1, T item2, params T[] items)
35+
public override void AddThese(T item1, T item2, params T[] items)
3636
{
3737
using var write = RWLock.WriteLock();
3838
base.Add(item1);

source/Synchronized/TrackedCollectionWrapper.cs

Lines changed: 84 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ public class TrackedCollectionWrapper<T, TCollection>
1616
{
1717
protected TCollection InternalSource;
1818

19+
/// <summary>
20+
/// Event fired after a chnage or group of changes has been made.
21+
/// </summary>
22+
public event EventHandler? Modified;
23+
24+
/// <summary>
25+
/// Event fired after an item has been added or removed from the collection.
26+
/// </summary>
27+
public event EventHandler<ItemChangedEventArgs<T>>? Changed;
28+
29+
protected bool HasChangedListeners => Changed is not null;
30+
31+
/// <summary>
32+
/// Event fired after the collection has been cleared.
33+
/// </summary>
34+
public event EventHandler<int>? Cleared;
35+
1936
[ExcludeFromCodeCoverage]
2037
public TrackedCollectionWrapper(TCollection collection, ModificationSynchronizer? sync = null)
2138
: base(sync) => InternalSource = collection ?? throw new ArgumentNullException(nameof(collection));
@@ -36,10 +53,13 @@ protected override void OnDispose()
3653
{
3754
base.OnDispose();
3855
Nullify(ref InternalSource); // Eliminate risk from wrapper.
56+
Modified = null;
57+
Changed = null;
58+
Cleared = null;
3959
}
4060

4161
[MethodImpl(MethodImplOptions.AggressiveInlining)]
42-
private bool AssertIsAliveCore() => AssertIsAlive();
62+
protected bool AssertIsAlive() => base.AssertIsAlive();
4363

4464
/// <inheritdoc />
4565
public int Count
@@ -53,24 +73,60 @@ public int Count
5373
protected virtual void AddInternal(T item)
5474
=> InternalSource.Add(item);
5575

76+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
77+
protected override void OnModified()
78+
=> Modified?.Invoke(this, EventArgs.Empty);
79+
80+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
81+
protected void OnChanged(ItemChange change, T item, int version)
82+
=> Changed?.Invoke(this, change.CreateArgs(item, version));
83+
84+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
85+
protected virtual void OnChanged<TIndex>(ItemChange change, TIndex index, T item, int version)
86+
=> Changed?.Invoke(this, change.CreateArgs(index, item, version));
87+
88+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
89+
private void OnAdded(T item, int version)
90+
=> OnChanged(ItemChange.Added, item, version);
91+
92+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
93+
private void OnRemoved(T item, int version)
94+
=> OnChanged(ItemChange.Removed, item, version);
95+
96+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
97+
protected virtual void OnCleared(int version)
98+
=> Cleared?.Invoke(this, version);
99+
56100
/// <inheritdoc />
57101
public void Add(T item)
58-
=> Sync!.Modifying(AssertIsAliveCore, () =>
59-
{
60-
AddInternal(item);
61-
return true;
62-
});
63-
64-
/// <inheritdoc cref="IAddMultiple{T}.Add(T, T, T[])" />
65-
public void Add(T item1, T item2, params T[] items)
66-
=> Sync!.Modifying(AssertIsAliveCore, () =>
67-
{
68-
AddInternal(item1);
69-
AddInternal(item2);
70-
foreach (T? i in items)
71-
AddInternal(i);
72-
return true;
73-
});
102+
=> Sync!.Modifying(
103+
AssertIsAlive,
104+
() =>
105+
{
106+
AddInternal(item);
107+
return true;
108+
},
109+
version => OnAdded(item, version));
110+
111+
/// <inheritdoc cref="IAddMultiple{T}.AddThese(T, T, T[])" />
112+
public void AddThese(T item1, T item2, params T[] items)
113+
=> Sync!.Modifying(AssertIsAlive,
114+
() =>
115+
{
116+
AddInternal(item1);
117+
AddInternal(item2);
118+
foreach (T? i in items)
119+
AddInternal(i);
120+
return true;
121+
},
122+
version =>
123+
{
124+
if (Changed is null) return;
125+
OnAdded(item1, version);
126+
OnAdded(item2, version);
127+
foreach (T? i in items)
128+
OnAdded(i, version);
129+
});
74130

75131
/// <inheritdoc />
76132
public void AddRange(IEnumerable<T> items)
@@ -87,11 +143,16 @@ public void AddRange(IEnumerable<T> items)
87143
if (enumerable.Count == 0)
88144
return;
89145

90-
Sync!.Modifying(AssertIsAliveCore, () =>
146+
Sync!.Modifying(AssertIsAlive, () =>
91147
{
92148
foreach (var item in enumerable)
93149
AddInternal(item);
94150
return true;
151+
},
152+
version =>
153+
{
154+
foreach (var item in enumerable)
155+
OnAdded(item, version);
95156
});
96157
}
97158

@@ -109,7 +170,7 @@ public void Clear()
109170
bool hasItems = count != 0;
110171
if (hasItems) ClearInternal();
111172
return hasItems;
112-
});
173+
}, OnCleared);
113174

114175
/// <inheritdoc />
115176
public bool Contains(T item)
@@ -126,20 +187,13 @@ public void CopyTo(T[] array, int arrayIndex)
126187
/// <inheritdoc />
127188
public virtual bool Remove(T item)
128189
=> Sync!.Modifying(
129-
AssertIsAliveCore,
130-
() => InternalSource.Remove(item));
131-
132-
[ExcludeFromCodeCoverage]
133-
protected virtual IEnumerator<T> GetEnumeratorInternal()
134-
=> InternalSource.GetEnumerator();
190+
AssertIsAlive,
191+
() => InternalSource.Remove(item),
192+
version => OnRemoved(item, version));
135193

136194
/// <inheritdoc />
137195
public IEnumerator<T> GetEnumerator()
138-
=> Sync!.Reading(() =>
139-
{
140-
AssertIsAlive();
141-
return GetEnumeratorInternal();
142-
});
196+
=> InternalSource.GetEnumerator();
143197

144198
[ExcludeFromCodeCoverage]
145199
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

0 commit comments

Comments
 (0)