diff --git a/GoRogue/Components/ParentAware/IParentAwareComponent.cs b/GoRogue/Components/ParentAware/IParentAwareComponent.cs index 152d6a65..d13fceeb 100644 --- a/GoRogue/Components/ParentAware/IParentAwareComponent.cs +++ b/GoRogue/Components/ParentAware/IParentAwareComponent.cs @@ -3,7 +3,7 @@ namespace GoRogue.Components.ParentAware { /// - /// Optional interface for components that are attached to something implementing . + /// Optional interface for components that are attached to something via a components field. /// /// /// While the implementation of this interface is not required for GoRogue components, when it is used with something @@ -22,6 +22,6 @@ public interface IParentAwareComponent /// . It is set automatically when added/removed from an object's /// component collection. /// - public IObjectWithComponents? Parent { get; set; } + public object? Parent { get; set; } } } diff --git a/GoRogue/Components/ParentAware/ParentAwareComponentBase.cs b/GoRogue/Components/ParentAware/ParentAwareComponentBase.cs index 3fc33c28..708cd4a7 100644 --- a/GoRogue/Components/ParentAware/ParentAwareComponentBase.cs +++ b/GoRogue/Components/ParentAware/ParentAwareComponentBase.cs @@ -10,7 +10,6 @@ namespace GoRogue.Components.ParentAware /// The type of the parent being passed. [PublicAPI] public class ParentAwareComponentRemovedEventArgs : EventArgs - where T : IObjectWithComponents { /// /// The parent from which the object was detached. @@ -33,6 +32,17 @@ public ParentAwareComponentRemovedEventArgs(T oldParent) /// type-checking of parent, or requiring that the object it's attached to has or does not have certain types of /// components. /// + /// + /// This class may be added as a component to any class that implements , however components + /// inheriting from this class will not implicitly fail in any way if it is used with a class that does not implement that interface. + /// Certain functions such as do require that their parent implement IObjectWithComponents, + /// and in general there is no guarantee that the Parent field gets updated if the parent doesn't implement that interface; however + /// you can sync the parent field manually as applicable. + /// + /// In most cases, the intended usage is to add this as a component to a class that implements ; + /// however the option of not doing so and manually syncing the Parent field instead allows this functionality to be tied into other + /// component systems as well. + /// [PublicAPI] public class ParentAwareComponentBase : IParentAwareComponent { @@ -44,13 +54,13 @@ public class ParentAwareComponentBase : IParentAwareComponent /// /// Fires when the component is unattached from an object /// - public event EventHandler>? Removed; + public event EventHandler>? Removed; - private IObjectWithComponents? _parent; + private object? _parent; /// /// The object the component is attached to. /// - public virtual IObjectWithComponents? Parent + public virtual object? Parent { get => _parent; set @@ -62,14 +72,13 @@ public virtual IObjectWithComponents? Parent var oldValue = _parent; _parent = value; // Null for oldValue is not possible because value == null AND value != _parent - Removed?.Invoke(this, new ParentAwareComponentRemovedEventArgs(oldValue!)); + Removed?.Invoke(this, new ParentAwareComponentRemovedEventArgs(oldValue!)); } else { if (_parent != null) - throw new Exception($"Components of type {nameof(ParentAwareComponentBase)} inherit from " + - $"{nameof(IParentAwareComponent)}, so they can't be attached to multiple " + - "objects simultaneously."); + throw new Exception($"Components of type {nameof(ParentAwareComponentBase)} " + + "can't be attached to multiple objects simultaneously."); _parent = value; Added?.Invoke(this, EventArgs.Empty); @@ -102,26 +111,47 @@ public static void ParentTypeCheck(object? s, EventArgs e) /// can't have multiple instances of itself attached to the same object by using /// Added += IncompatibleWith<MyOwnType>. /// + /// + /// This function only works if the parent is of type IObjectWithComponents. If the parent is not of that + /// type, this function will throw an exception. + /// /// Type of the component this one is incompatible with. /// /// public void IncompatibleWith(object? s, EventArgs e) where TComponent : class { - if (Parent!.GoRogueComponents.GetAll().Any(i => !ReferenceEquals(this, i))) + var parent = Parent as IObjectWithComponents; + if (parent == null) + throw new Exception($"{nameof(IncompatibleWith)} can only be used with components that are attached to " + + $"objects that implement {nameof(IObjectWithComponents)}."); + + if (parent.GoRogueComponents.GetAll().Any(i => !ReferenceEquals(this, i))) throw new Exception($"{GetType().Name} components are marked as incompatible with {typeof(TComponent).Name} components, so the component couldn't be added."); } } /// - /// Optional base class for components attached to a a class implementing . + /// Optional base class for components attached to a class implementing . /// Adds all functionality of , and additionally type-checks the object it's /// attached to to make sure it is of the given type. It also exposes its property as that type - /// instead of IObjectWithComponents. + /// instead of object. /// + /// + /// Like , subclasses of this class may be added as a component to any class that implements + /// , however components inheriting from this class will not implicitly fail in any way if it is + /// used with a class that does not implement that interface. Certain functions such as + /// do require that their parent implement IObjectWithComponents, + /// and in general there is no guarantee that the Parent field gets updated if the parent doesn't implement that interface; however + /// you can sync the parent field manually as applicable. + /// + /// In most cases, the intended usage is to add this as a component to a class that implements ; + /// however the option of not doing so and manually syncing the Parent field instead allows this functionality to be tied into other + /// component systems as well. + /// /// Type of the component's parent. [PublicAPI] - public class ParentAwareComponentBase : ParentAwareComponentBase where TParent : class, IObjectWithComponents + public class ParentAwareComponentBase : ParentAwareComponentBase where TParent : class { /// /// The object the component is attached to. @@ -147,7 +177,7 @@ public ParentAwareComponentBase() base.Removed += OnRemoved; } - private void OnRemoved(object? sender, ParentAwareComponentRemovedEventArgs e) + private void OnRemoved(object? sender, ParentAwareComponentRemovedEventArgs e) => Removed?.Invoke(sender, new ParentAwareComponentRemovedEventArgs((TParent)e.OldParent)); } } diff --git a/GoRogue/GoRogue.csproj b/GoRogue/GoRogue.csproj index b933c5b2..c5df9d99 100644 --- a/GoRogue/GoRogue.csproj +++ b/GoRogue/GoRogue.csproj @@ -11,7 +11,7 @@ Configure versioning information, making sure to append "debug" to Debug version to allow publishing to NuGet seperately from Release version. --> - 3.0.0-beta05 + 3.0.0-beta06 $(Version)-debug diff --git a/changelog.md b/changelog.md index e79ba101..1a683071 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). None +## [3.0.0-beta06] - 2023-05-22 + +### Changed +- `IParentAwareComponent` and `ParentAwareComponentBase` now record the parent as type `object` +- `ParentAwareComponentBase` no longer has type restrictions on type T; it can now accept any type as the parent. + - This makes integration into other component systems more feasible since `IObjectWithComponents` is no longer required + ## [3.0.0-beta05] - 2023-05-17 ### Added