89 lines
3.3 KiB
C#
89 lines
3.3 KiB
C#
using System.Collections.ObjectModel;
|
|
|
|
namespace Prefab.Base;
|
|
|
|
/// <summary>
|
|
/// Provides a customizable attribute bag with change tracking for Prefab domain objects.
|
|
/// </summary>
|
|
public class GenericAttributes : ICanBeCustomized
|
|
{
|
|
private readonly Dictionary<string, object> _attributes = new();
|
|
private readonly List<GenericAttributeChange> _changes = new();
|
|
|
|
/// <inheritdoc />
|
|
IReadOnlyDictionary<string, object> IHasGenericAttributes.GenericAttributes => new ReadOnlyDictionary<string, object>(_attributes);
|
|
|
|
/// <summary>
|
|
/// Gets the ordered list of attribute mutations recorded since the last call to <see cref="ClearChanges"/>.
|
|
/// </summary>
|
|
public IReadOnlyList<GenericAttributeChange> Changes => _changes.AsReadOnly();
|
|
|
|
/// <summary>
|
|
/// Provides an extension point for enforcing key/value requirements before persisting the attribute.
|
|
/// </summary>
|
|
/// <param name="key">The attribute key being modified.</param>
|
|
/// <param name="value">The proposed attribute value.</param>
|
|
protected virtual void ValidateAttribute(string key, object value)
|
|
{
|
|
// Default: no validation. Override in derived classes for custom logic.
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets or replaces an attribute value and records the mutation.
|
|
/// </summary>
|
|
/// <param name="key">The attribute key to set.</param>
|
|
/// <param name="value">The attribute value.</param>
|
|
public void SetAttribute(string key, object value)
|
|
{
|
|
_attributes.TryGetValue(key, out var oldValue);
|
|
ValidateAttribute(key, value);
|
|
_attributes[key] = value;
|
|
_changes.Add(new GenericAttributeChange(key, oldValue, value));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an attribute if present and records the removal.
|
|
/// </summary>
|
|
/// <param name="key">The attribute key to remove.</param>
|
|
/// <returns><c>true</c> when the key existed and was removed; otherwise <c>false</c>.</returns>
|
|
public bool RemoveAttribute(string key)
|
|
{
|
|
if (_attributes.TryGetValue(key, out var oldValue) && _attributes.Remove(key))
|
|
{
|
|
_changes.Add(new GenericAttributeChange(key, oldValue, null));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to read an attribute value and cast it to the requested type.
|
|
/// </summary>
|
|
/// <param name="key">The attribute key to read.</param>
|
|
/// <param name="value">Receives the typed attribute value when successful.</param>
|
|
/// <typeparam name="T">The expected attribute type.</typeparam>
|
|
/// <returns><c>true</c> when the attribute exists and matches the requested type.</returns>
|
|
public bool TryGetAttribute<T>(string key, out T value)
|
|
{
|
|
if (_attributes.TryGetValue(key, out var obj) && obj is T t)
|
|
{
|
|
value = t;
|
|
return true;
|
|
}
|
|
|
|
value = default!;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an immutable snapshot copy of the current attributes.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, object> Snapshot() => new ReadOnlyDictionary<string, object>(new Dictionary<string, object>(_attributes));
|
|
|
|
/// <summary>
|
|
/// Clears all recorded attribute changes while keeping the current attribute values intact.
|
|
/// </summary>
|
|
public void ClearChanges() => _changes.Clear();
|
|
}
|