I have a pretty basic understanding of inheritance and so when using it there are a few moments like this where I find it difficult to understand what's fully happening and it probably doesn't help that I'm most likely not using it properly.
Anyways though I have these 3 classes
public abstract class EffectBase
{
public enum EffectType
{
harm,
help,
self
}
public EffectType type;
public float duration;
public void Activate()
{
Debug.Log("Activating effect");
ApplyEffect();
}
public abstract void ApplyEffect();
public abstract void End();
}
public class Player : MonoBehaviour
{
public List<EffectBase> effects = new List<EffectBase>();
void Update()
{
if (Input.GetKeyDown("q"))
{
Debug.Log("Q pressed");
AddEffect(new SpeedEffect());
}
}
public void AddEffect(EffectBase effect)
{
Debug.Log("Adding effect");
effects.Add(effect);
effect.Activate();
}
}
public class SpeedEffect : EffectBase
{
public override void ApplyEffect()
{
Debug.Log("Speed effect applied");
}
public override void End()
{
Debug.Log("Speed effect ended");
}
}
When I call the AddEffect method I pass a new instance of SpeedEffect (I think it's an instance) as the parameter and then in the AddEffect method I call the Activate method from it, however, in the SpeedEffect class, it doesn't have or override that method so I'm assuming it goes to the base class which does have it and continues and now here's where I get confused in the Activate method it calls the ApplyEffect method, but how does it know to call the one in the SpeedEffect class?
Despite not having an override for Activate() that method still exists in the SpeedEffect class, you just didn't need to write any code for it since its the same code so theres no need to duplicate it.
The code for Activate() calls the ApplyEffect() method for whatever class its being called from. In this case: EffectBase.Activate() and SpeedEffect.Activate() have the same code in terms of reading it, but they are not the same; EffectBase.ApplyEffect() and SpeedEffect.ApplyEffect() are two different methods and each are called from their respective classes.
Related
I'm working on a hobby project in Unity. I have MonoBehaviour scripts for my characters that use component objects for each behavior the character has. When I create new characters, I inherit a new class from the base class for whichever component behaves differently.
When certain triggers occur, I send characters back to their initial state by calling a Reset() method exposed by the base class that sets fields back to their initial values. I'm wondering how to send that call down through the inheritance chain. Right now, the base class has a protected virtual ChildReset() that gets called in Reset() and does nothing by default. If child classes have fields to reset, they override this method. It feels like a really awkward way of doing it.
I like the idea of implementing something similar to the messaging system Unity uses. If a monobehavior doesn't use the Update() message, then the class just doesn't define an update. It eliminates unnecessary calls. I have no idea how I would do something like that.
Any thought invested in this is much appreciated! I've written out the way my project is structured below just in case these details are useful for answers.
public class Character : MonoBehaviour
{
private Motion motionController;
private Interaction characterInteractionController;
//etc
private void Update()
{
motionController.DoStuff();
characterInteractionController.DoStuff();
}
private void Reset()
{
motionController.Reset();
characterInteractionController.Reset();
}
private void OnEnable() => ResetTrigger.OnReset += Reset;
private void OnDisable() => ResetTrigger.OnReset -= Reset;
}
public class Motion : Component {}
public class Interaction : Component {}
public abstract class Component
{
public void Reset()
{
/* set fields to default values */
ChildReset();
}
protected virtual void ChildReset() { }
public abstract void DoStuff();
}
There is no need to send a call down through the inheritance chain. You do not have two different objects. An object of the child class contains everything declared in the base class. Why not directly make Reset() virtual?
public abstract class Character : MonoBehaviour
{
public virtual void Reset()
{
...
}
}
public class ChildCharacter : Character
{
// If ChildCharacter has stuff to reset, override this method, otherwise don't!
public override void Reset()
{
base.Reset(); // Call this to reset stuff from the base class.
//TODO: reset child stuff.
}
}
If Reset is overridden in the child class, then calling Reset will call ChildCharacter.Reset() even if called on a variable statically typed as Character.
Character c = new ChildCharacter();
c.Reset(); // calls ChildCharacter.Reset() when overridden
If Reset is not overridden in the child class, then calling Reset will call Character.Reset() even if called on a ChildCharacter.
ChildCharacter child = new ChildCharacter();
child.Reset(); // calls Character.Reset() when not overridden.
So I've been working on a state machine for a player character in a Unity game, and thought a hierarchical system would work well. However, I'm having some trouble getting methods to traverse the inheritance chain. If someone could point out my mistake for me, I'd greatly appreciate it.
Here's my PlayerScript : Monobehavior script:
public PlayerState State;
public void Start() {
State = new StoppedState();
State.Enter(this);
}
public void Update() {
State.Update(this);
}
And here are the PlayerStates:
public class PlayerState {
public virtual PlayerState Update(PlayerScript player) {
Debug.Log("Blorp");
return null;
}
public virtual void Enter(PlayerScript player) {}
}
public class LowPriorityState : PlayerState {
public new virtual PlayerState Update(PlayerScript player) {
Debug.Log("Blop");
PlayerState newState = base.Update(player);
//Do state stuff, determine if state should change
return newState;
}
}
public class StoppedState : LowPriorityState {
public override PlayerState Update(PlayerScript player) {
Debug.Log("Blip");
PlayerState newState = base.Update(player);
//Do state stuff, determine if state should change
return newState;
}
public override void Enter(PlayerScript player) {
Debug.Log("Entered Stopped State");
}
}
The expected output after 1 frame is:
Entered Stopped State
Blip
Blop
Blorp
but instead I'm getting:
Entered Stopped State
Blorp
...which is completely baffling, because it's clearly using the overridden "Enter" method but not using the overridden "Update" method. Does anyone know what I'm doing wrong and how to fix it?
The "new" modifier is used to hide a method of the base class and create a completely new method. This way, if the variable is of type BaseClass, it will call the method in the base class, and if the variable is of type DerivedClass, it will call the method in the derived class.
So in the LowPriorityState you are creating a new method, not overriding the one from PlayerState. So your field State of type PlayerState will continue to call the method from the base class.
And then you override this new method in StoppedState, but as your field State is of type PlayerState, it continues to call the method from the base class.
So just replace the new keyword with override.
I have a concrete class call AncSubscriberWrapper and it has a call back method
public Task OnUnshownCounterUpdatedAsync(long counter)
{
return Task.CompletedTask;
}
and also I have an abstract class call BasePageViewModel and it has an async method
protected async void GetUnseenNotificationsCount()
{
UnseenNotificationCount = await m_ancSubscriberWrapper.TryGetNotificationCountAsync(NotificationStatus.Delivered | NotificationStatus.Created);
}
Classes paths are like below inside the project.
./src/Visit.Common/Services/AncSubscriberWrapper.cs
./src/Visit.Common/ViewModels/PageViewModels/BasePageViewModel.cs
What I need is to execute the GetUnseenNotificationsCount() method from OnUnshownCounterUpdatedAsync() method.
How can I do that?
This is one way I was able to reach my goal. I am not sure this is the right way of doing this though.
How I achieved my goal is using an EventHandler.
Create an interface (IAncSubscriberWrapper) for AncSubscriberWrapper class
public interface IAncSubscriberWrapper
{
event EventHandler UnshownNotificationCounterUpdated;
}
and implements it like below
public class AncSubscriberWrapper : NotificationCenterFacade, IAncSubscriberWrapper
Then implement the interface member inside the AncSubscriberWrapper class
public event EventHandler UnshownNotificationCounterUpdated;
and also invoke method inside the OnUnshownCounterUpdatedAsync method
public Task OnUnshownCounterUpdatedAsync(long counter)
{
UnshownNotificationCounterUpdated?.Invoke(this, null);
return Task.CompletedTask;
}
Then go to BasePageViewModel and register and unregister listeners.
public void Initialize()
{
m_ancSubscriberWrapper.UnshownNotificationCounterUpdated += OnUnshownNotificationCounterUpdated;
}
public void Teardown()
{
m_ancSubscriberWrapper.UnshownNotificationCounterUpdated -= OnUnshownNotificationCounterUpdated;
}
Since BasePageViewModel is an abstract class, which ever the concrete class that use it, will override the methods. (Following code snippet is not directly related to this question, but for understanding the design on my code basis)
/*
* BaseContainerViewController is only accepting TPageViewModel generic type of classes only.
* TPageViewModel inherits BasePageViewModel and BaseContainerViewController inherits BaseViewController
* That's what following line is all about
*/
public abstract class BaseContainerViewController<TPageViewModel> : BaseViewController where TPageViewModel : BasePageViewModel
and
public class WardListViewController : BaseContainerViewController<WardListPageViewModel>
Back to the final part of the answer. Add following method inside BasePageViewModel class and that will complete the process.
protected virtual void OnUnshownNotificationCounterUpdated(object sender, EventArgs eventArgs)
{
GetUnseenNotificationsCount();
}
In this question I mention Unity3D, but applies generally in c#.
Unity3D has an Interface that looks like this ..
public class SomeRobot:MonoBehaviour, IPointerDownHandler
{
public void OnPointerDown(PointerEventData data)
{
Debug.Log("Gets called whenever someone touches the screen...");
}
No problem so far.
I make my own interface..
public interface IFUHandler:IPointerDownHandler
{
void OnBlah (PointerEventData data);
}
Assume I have a daemon or whatever that calls OnBlah for consumers of the interface.
public class SomeRobot:MonoBehaviour, IFUHandler
{
public void OnBlah(PointerEventData data)
{
Debug.Log("Gets called when blah happens...");
}
No problem so far. BUT
public class SomeRobot:MonoBehaviour, IFUHandler
{
public void OnBlah(PointerEventData data)
{
Debug.Log("Gets called when blah happens...");
}
public void OnPointerDown(PointerEventData data)
{
Debug.Log("this DOES still get called also...");
}
In fact, OnPointerDown still gets called. Which makes sense.
BUT .... is there a way for my Interface to eat the calls to OnPointerDown?
So that a consumer subscribing to IFUHandler in fact does NOT get the calls to OnPointerDown?
(But of course gets my OnBlah calls.)
Or am I just suffering "TMIOOK" thinking? (Tied Myself In OO Knots thinking :) )
Simple answer: No.
Possible solutions:
Don't inherit IPointerDownHandler when you don't need it. Why do you need this interface if you aren't going to use it?
Create a Sealed Abstract class with a non-virtual method
public class BaseFUHandler : MonoBehaviour, IFUHandler
{
public abstract void OnBlah(PointerEventData data);
public void OnPointerDown(PointerEventData data)
{
//Gets called, but nothing hapends.
}
Given the following pseudo C# code:
public abstract class Worker
{
public void DoWork()
{
Prepare();
Work();
Cleanup();
}
protected abstract void Work();
}
With this design, it is possible, that a derived class (which overrides the Work() method) calls the Work() method directly.
I'm looking for design tips on how to prevent any subclass to do this call, or at least generate a compiler warning.
Why I want to do this?
The Worker actually dispatches the Work() to another thread. DoWork() takes care about the locking.
I know, this is weird. The Work()-body might just consist of a method call to DoMyWork() and the object might call DoMyWork() somewhere else. It's impossible to intercept that.
Anyways. I appreciate any thoughts on that issue.
Thanks to Eris and Corey I think I figured it out. What about this way:
public abstract class Worker
{
public void DoWork()
{
if (Work != null)
{
Prepare();
Work();
Cleanup();
}
}
protected Action Work { private get; set; }
}
public class ImplWorker {
public ImplWorker() {
Work = //whatever
}
}
Still if he really wanted to he can store it somewhere else and stuff. But it goes as far as I can imagine.
If the Work is done using a resource (e.g. remote server, database connection), or it can be changed to be like that, then you could do it like this:
public interface IResource
{
}
public abstract class Worker
{
private class TheResource : IResource
{
// implement
}
public void DoWork()
{
Prepare();
var resObj = new TheResource();
Work(resObj);
// if needed, cleanup/dispose resObj here
Cleanup();
}
protected abstract void Work(IResource resourceObj);
}
This is somewhat of a hack, but you could change the method to be virtual, add a body which calls the new method, and then add an ObsoleteAttribute to it.
protected void DoWork()
{
// New method
}
[Obsolete("This method is deprecated.")]
protected virtual void Work()
{
DoWork();
}
Marking the method as virtual will prevent any existing child classes from breaking, since they were previously required to override Work().