Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
Comment on lines -1 to -2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like your IDE ate the header

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Stride.Core.Collections;

public interface ITrackingCollectionChanged
public interface ITrackingCollectionChanged<TValue>
{
/// <summary>
/// Occurs when [collection changed].
/// </summary>
/// Called as is when adding an item, and in reverse-order when removing an item.
event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged;
event EventHandler<TrackingCollectionChangedEventArgs<TValue>> CollectionChanged;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

namespace Stride.Core.Collections;

public interface ITrackingKeyedCollectionChanged<TKey, TValue>
{
/// <summary>
/// Occurs when [collection changed].
/// </summary>
/// Called as is when adding an item, and in reverse-order when removing an item.
event EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>> CollectionChanged;
}
32 changes: 16 additions & 16 deletions sources/core/Stride.Core/Collections/TrackingCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,38 @@ namespace Stride.Core.Collections;
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
[DataSerializer(typeof(ListAllSerializer<,>), Mode = DataSerializerGenericMode.TypeAndGenericArguments)]
public class TrackingCollection<T> : FastCollection<T>, ITrackingCollectionChanged
public class TrackingCollection<T> : FastCollection<T>, ITrackingCollectionChanged<T>
{
private EventHandler<TrackingCollectionChangedEventArgs>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs>? itemRemoved;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? _itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? _itemRemoved;
Comment on lines +17 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong naming convention

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah my mistake, I thought we were going to use the Microsoft standards for new changes if we worked in them. Ill switch them back to the lower camel case structure.


/// <inheritdoc/>
public event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged
public event EventHandler<TrackingCollectionChangedEventArgs<T>> CollectionChanged
{
add
{
// We keep a list in reverse order for removal, so that we can easily have multiple handlers depending on each others
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(value, itemRemoved);
_itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(_itemAdded, value);
_itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(value, _itemRemoved);
}
remove
{
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemRemoved, value);
_itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(_itemAdded, value);
_itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(_itemRemoved, value);
}
}

/// <inheritdoc/>
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, null, index, true));
_itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, item, default, index, true));
}

/// <inheritdoc/>
protected override void RemoveItem(int index)
{
itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, this[index], null, index, true));
_itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, this[index], default, index, true));
base.RemoveItem(index);
}

Expand All @@ -57,25 +57,25 @@ protected override void ClearItems()
protected void ClearItemsEvents()
{
// Note: Changing CollectionChanged is not thread-safe
var collectionChanged = itemRemoved;
var collectionChanged = _itemRemoved;
if (collectionChanged != null)
{
for (var i = Count - 1; i >= 0; --i)
collectionChanged(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, this[i], null, i, true));
collectionChanged(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, this[i], default, i, true));
}
}

/// <inheritdoc/>
protected override void SetItem(int index, T item)
{
// Note: Changing CollectionChanged is not thread-safe
var collectionChangedRemoved = itemRemoved;
var collectionChangedRemoved = _itemRemoved;

object? oldItem = collectionChangedRemoved != null ? this[index] : null;
collectionChangedRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, null, index, false));
T? oldItem = collectionChangedRemoved != null ? this[index] : default;
collectionChangedRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, oldItem, default, index, false));

base.SetItem(index, item);

itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, oldItem, index, false));
_itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, item, oldItem, index, false));
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

Comment on lines -1 to -3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to worry that you aren't feeding your IDE correctly

using System.Collections.Specialized;

namespace Stride.Core.Collections;

public class TrackingCollectionChangedEventArgs : EventArgs
public sealed class TrackingCollectionChangedEventArgs<TValue> : EventArgs
{
public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action, object? item, object? oldItem, int index, bool collectionChanged)
public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action, TValue? item, TValue? oldItem, int index, bool collectionChanged)
{
Action = action;
Item = item;
OldItem = oldItem;
Key = null;
Index = index;
CollectionChanged = collectionChanged;
}

public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action, object key, object? item, object? oldItem, bool collectionChanged)
{
Action = action;
Item = item;
OldItem = oldItem;
Key = key;
Index = -1;
CollectionChanged = collectionChanged;
}

/// <summary>
/// Gets the type of action performed.
/// Allowed values are <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>.
Expand All @@ -36,15 +21,12 @@ public TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction action,
/// <summary>
/// Gets the added or removed item (if dictionary, value only).
/// </summary>
public object? Item { get; }
public TValue? Item { get; }

/// <summary>
/// Gets the previous value. Only valid if <see cref="Action"/> is <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>
/// </summary>
public object? OldItem { get; }

/// <summary>Gets the added or removed key (if dictionary).</summary>
public object? Key { get; }
public TValue? OldItem { get; }

/// <summary>
/// Gets the index in the collection (if applicable).
Expand Down
32 changes: 16 additions & 16 deletions sources/core/Stride.Core/Collections/TrackingDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@ namespace Stride.Core.Collections;
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
[DataSerializer(typeof(DictionaryAllSerializer<,,>), Mode = DataSerializerGenericMode.TypeAndGenericArguments)]
public class TrackingDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, ITrackingCollectionChanged
public class TrackingDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, ITrackingKeyedCollectionChanged<TKey, TValue>
where TKey : notnull
{
private readonly Dictionary<TKey, TValue> innerDictionary;

private EventHandler<TrackingCollectionChangedEventArgs>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs>? itemRemoved;
private EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>? _itemAdded;
private EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>? _itemRemoved;
Comment on lines +26 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, same here


/// <inheritdoc/>
public event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged
public event EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>> CollectionChanged
{
add
{
// We keep a list in reverse order for removal, so that we can easily have multiple handlers depending on each others
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(value, itemRemoved);
_itemAdded = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>)Delegate.Combine(_itemAdded, value);
_itemRemoved = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>)Delegate.Combine(value, _itemRemoved);
}
remove
{
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemRemoved, value);
_itemAdded = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>?)Delegate.Remove(_itemAdded, value);
_itemRemoved = (EventHandler<TrackingKeyedCollectionChangedEventArgs<TKey, TValue>>?)Delegate.Remove(_itemRemoved, value);
}
}

Expand All @@ -54,7 +54,7 @@ public TrackingDictionary()
public void Add(TKey key, TValue value)
{
innerDictionary.Add(key, value);
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, key, value, null, true));
_itemAdded?.Invoke(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Add, key, value, default, true));
}

/// <inheritdoc/>
Expand All @@ -72,9 +72,9 @@ public ICollection<TKey> Keys
/// <inheritdoc/>
public bool Remove(TKey key)
{
var collectionChanged = itemRemoved;
var collectionChanged = _itemRemoved;
if (collectionChanged != null && innerDictionary.TryGetValue(key, out var dictValue))
collectionChanged(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, key, dictValue, null, true));
collectionChanged(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Remove, key, dictValue, default, true));

return innerDictionary.Remove(key);
}
Expand All @@ -100,17 +100,17 @@ public TValue this[TKey key]
}
set
{
var collectionChangedRemoved = itemRemoved;
var collectionChangedRemoved = _itemRemoved;
if (collectionChangedRemoved != null)
{
var alreadyExisting = innerDictionary.TryGetValue(key, out var oldValue);
if (alreadyExisting)
collectionChangedRemoved(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, key, oldValue, null, false));
collectionChangedRemoved(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Remove, key, oldValue, default, false));

innerDictionary[key] = value;

// Note: CollectionChanged is considered not thread-safe, so no need to skip if null here, shouldn't happen
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, key, innerDictionary[key], oldValue, !alreadyExisting));
_itemAdded?.Invoke(this, new TrackingKeyedCollectionChangedEventArgs<TKey, TValue>(NotifyCollectionChangedAction.Add, key, innerDictionary[key], oldValue, !alreadyExisting));
}
else
{
Expand All @@ -128,7 +128,7 @@ public void Add(KeyValuePair<TKey, TValue> item)
/// <inheritdoc/>
public void Clear()
{
var collectionChanged = itemRemoved;
var collectionChanged = _itemRemoved;
if (collectionChanged != null)
{
foreach (var key in innerDictionary.Keys.ToArray())
Expand Down Expand Up @@ -169,7 +169,7 @@ public bool IsReadOnly
/// <inheritdoc/>
public bool Remove(KeyValuePair<TKey, TValue> item)
{
var collectionChanged = itemRemoved;
var collectionChanged = _itemRemoved;
if (collectionChanged != null && innerDictionary.Contains(item))
return innerDictionary.Remove(item.Key);

Expand Down
22 changes: 11 additions & 11 deletions sources/core/Stride.Core/Collections/TrackingHashSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ namespace Stride.Core.Collections;
/// Underlying storage is done with a <see cref="HashSet{T}"/>.
/// </remarks>
/// <typeparam name="T">The type of elements in the hash set.</typeparam>
public class TrackingHashSet<T> : ISet<T>, IReadOnlySet<T>, ITrackingCollectionChanged
public class TrackingHashSet<T> : ISet<T>, IReadOnlySet<T>, ITrackingCollectionChanged<T>
{
private readonly HashSet<T> innerHashSet = [];

private EventHandler<TrackingCollectionChangedEventArgs>? itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs>? itemRemoved;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? _itemAdded;
private EventHandler<TrackingCollectionChangedEventArgs<T>>? _itemRemoved;
Comment on lines +19 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup


/// <inheritdoc/>
public event EventHandler<TrackingCollectionChangedEventArgs> CollectionChanged
public event EventHandler<TrackingCollectionChangedEventArgs<T>> CollectionChanged
{
add
{
// We keep a list in reverse order for removal, so that we can easily have multiple handlers depending on each others
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>)Delegate.Combine(value, itemRemoved);
_itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(_itemAdded, value);
_itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>)Delegate.Combine(value, _itemRemoved);
}
remove
{
itemAdded = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemAdded, value);
itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs>?)Delegate.Remove(itemRemoved, value);
_itemAdded = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(_itemAdded, value);
_itemRemoved = (EventHandler<TrackingCollectionChangedEventArgs<T>>?)Delegate.Remove(_itemRemoved, value);
}
}

Expand All @@ -40,7 +40,7 @@ public bool Add(T item)
{
if (innerHashSet.Add(item))
{
itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, null, -1, true));
_itemAdded?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, item, default, -1, true));
return true;
}

Expand Down Expand Up @@ -116,7 +116,7 @@ void ICollection<T>.Add(T item)
/// <inheritdoc/>
public void Clear()
{
if (itemRemoved != null)
if (_itemRemoved != null)
{
foreach (var item in innerHashSet.ToArray())
{
Expand Down Expand Up @@ -153,7 +153,7 @@ public bool Remove(T item)
if (!innerHashSet.Remove(item))
return false;

itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, null, -1, true));
_itemRemoved?.Invoke(this, new TrackingCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, item, default, -1, true));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System.Collections.Specialized;

namespace Stride.Core.Collections;

public sealed class TrackingKeyedCollectionChangedEventArgs<TKey, TValue> : EventArgs
{
public TrackingKeyedCollectionChangedEventArgs(NotifyCollectionChangedAction action, TKey key, TValue? item, TValue? oldItem, bool collectionChanged)
{
Action = action;
Item = item;
OldItem = oldItem;
Key = key;
Index = -1;
CollectionChanged = collectionChanged;
}

/// <summary>
/// Gets the type of action performed.
/// Allowed values are <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>.
/// </summary>
public NotifyCollectionChangedAction Action { get; }

/// <summary>
/// Gets the added or removed item (if dictionary, value only).
/// </summary>
public TValue? Item { get; }

/// <summary>
/// Gets the previous value. Only valid if <see cref="Action"/> is <see cref="NotifyCollectionChangedAction.Add"/> and <see cref="NotifyCollectionChangedAction.Remove"/>
/// </summary>
public TValue? OldItem { get; }

/// <summary>Gets the added or removed key (if dictionary).</summary>
public TKey Key { get; }

/// <summary>
/// Gets the index in the collection (if applicable).
/// </summary>
public int Index { get; }

/// <summary>
/// Gets a value indicating whether [collection changed (not a replacement but real insertion/removal)].
/// </summary>
/// <value>
/// <c>true</c> if [collection changed]; otherwise, <c>false</c>.
/// </value>
public bool CollectionChanged { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private NavigationMeshDebugVisual CreateDebugVisual(NavigationMesh navigationMes
return ret;
}

private void GameSystemsOnCollectionChanged(object sender, TrackingCollectionChangedEventArgs e)
private void GameSystemsOnCollectionChanged(object sender, TrackingCollectionChangedEventArgs<IGameSystemBase> e)
{
if (dynamicNavigationMeshSystem != null)
return;
Expand Down
Loading