Problem with events (delegates) and inheritance in Unity C# - c#

I have class Base which the has event and delegate. It is a base class for class Button. Also, I have class Receiving, inside this class I subscribe to event which is inside the class Base. For some reason this throws an error on the line where I call the event.
Class Base:
public class Base : MonoBehaviour
{
public delegate void SendSave(string root);
public event SendSave sendSave;
public Text _text;
private string _saveName;
public virtual void Tap()
{
MailingSave("Base");
}
public void MailingSave(string name)
{
_saveName = name;
sendSave(_saveName);
}
}
Class Button
public class Button : Base
{
public Text uiText;
public override void Tap()
{
string _name = uiText.text;
MailingSave(_name);
}
}
Class Receiving
public class Receiving : MonoBehaviour
{
public Base _base;
private void Start()
{
_base.sendSave += ToStringName;
}
private void ToStringName(string name)
{
Debug.Log(name);
}
}
The error occurs in the Base class on the line sendSave (_saveName);.

There is no obvious compiler error in your code so without further details I can just guess you get a NullReferenceException.
This happens if the sendSave is invoked before anything is attached as a listener/callback.
You should always first check if there is any listener at all
if(sendSave != null)
{
sendSave(_saveName);
}
which can also be written shorter as
sendSave?.Invoke(_saveName);
And then debug your code and make sure that the line
_base.sendSave += ToStringName;
is actually invoked at all and before the event gets invoked!
e.g. is there a Receiving component in your scene at all and is it active and enabled?
Update after your comments
If you rather want this event to be globally fired and listened for any of the Button or other child classes then I would rather use a static event like
// Note that I would also make this static
// which automatically makes sure that only inherited components may be actually
// attached to an object but not this base class
public abstract class Base : MonoBehaviour
{
// Additionally pass in which instance invoked this
public delegate void SendSave(Base sender, string root);
// Make this accessible static
public static event SendSave sendSave;
public Text _text;
private string _saveName;
public virtual void Tap()
{
MailingSave(nameof(Base));
}
public void MailingSave(string name)
{
_saveName = name;
sendSave?.Invoke(_saveName);
}
}
And now you can attach your listener globally by simply using
public class Receiving : MonoBehaviour
{
private void Start()
{
Base.sendSave += ToStringName;
}
private void ToStringName(Base sender, string name)
{
Debug.Log($"Sender: \"{sender}\" saveName: \"{name}\"", this);
}
}

Related

Subscribe method from assembly to an event in my application

Let's say I have an event, declared as so:
public class OnGooseHonkEvent{
public delegate void OnGooseHonk();
public static event OnGooseHonk OnGooseHonkEvent;
}
And then in an assembly compiled to a dll, I have the following code:
namespace ExampleMod
{
public class Class1
{
public Class1()
{
}
public void OnGooseHonk()
{
UntitledGooseAPI.Log("Goose Honked");
}
}
}
How would I be able to go about subscribing ExampleMod.Class1.OnGooseHonk to OnGooseHonkEvent.OnGooseHonkEvent? And it has to be done with the Assembly module, as I am loading all assemblies from a directory programatically.
I might be a little confused the way the OnGooseHonkEvent class is setup. Changing the delegate to a method that can be called on the object, and setting up the OnGooseHonkedEvent as a Eventhandler<EventArgs> instead of an event, I got this:
public class OnGooseHonkEvent
{
// Calling this will notify every event subscriber the goose has honked
public void HonkTheGoose()
{
OnGooseHonkedEvent?.Invoke(this, EventArgs.Empty);
}
public static event EventHandler<EventArgs> OnGooseHonkedEvent;
}
public class Class1
{
public Class1()
{
OnGooseHonkEvent.OnGooseHonkedEvent += OnGooseHonk;
}
public void OnGooseHonk(object sender, EventArgs e)
{
UntitledGooseAPI.Log("When any instance of OnGooseHonkEvent calls the HonkTheGoose() method, this should get fired");
}
}

How to pass abstract method as reference?

I will admit, i am doing homework and i am stuck on this one question (Part A). How do i pass the notice method as reference to the railway signal ? Can't i just find out which class was called in the abstract constructor and then print the class name within the notify method? For example:
RailwayUser
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.Writeline(className);
}
RailwaySignal Class
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
This kind of code / design is flawed, since what it does is RailwayUser, registers the object reference with the _watchers List in the RailWaySignal class, which in turn calls the public Notice method on each user when Notify is invoked, which is not how Event Signaling or Function Pointer works. In fact public _watchers is dangerous, as it can be cleared by any user, though that can be moderated using property access
Code with Issue
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
Following shall be the actual code using events and delegates:
Correct Version
Code Snippet Online - https://www.jdoodle.com/embed/v0/uEc
void Main()
{
List<RailwayUser> railwayUsers = new List<RailwayUser>();
railwayUsers.Add(new RailwayUser());
railwayUsers.Add(new RailwayUser());
RailwayUser.TestNotification();
}
public enum Colour
{
Red,
Green,
NoSignal
}
public class RailwaySignal
{
public string Name {get; set;}
public RailwaySignal(string railwaySignalName)
{
Name = railwaySignalName;
}
// Delegate for handling event
public delegate void RailwaySignalEventHandler(object source, Colour e);
// Delagate object for handling event
private RailwaySignalEventHandler _railwaySignalEvent;
// Event Accessor
public event RailwaySignalEventHandler RailwaySignalEvent
{
add
{
lock (this)
{
_railwaySignalEvent += value;
}
}
remove
{
lock (this)
{
_railwaySignalEvent -= value;
}
}
}
// Invoke Event for subscribed clients
private void Notify()
{
if (_railwaySignalEvent != null)
_railwaySignalEvent.Invoke(this, Colour.Green);
}
// Test the Event Invocation
public void TestEvent()
{
Notify();
}
}
public class RailwayUser
{
private static RailwaySignal railwaySignal { get; set;} = new RailwaySignal("Signal1");
public RailwayUser()
{
railwaySignal.RailwaySignalEvent += this.Notice;
}
public static void TestNotification()
{
railwaySignal.TestEvent();
}
public void Notice(object sender, Colour color)
{
Console.WriteLine($"Notice Called, Colour is :: {color}, Sender is :: {((RailwaySignal)sender).Name}");
}
}
Result
Notice Called, Colour is :: Green, Sender is :: Signal1
Notice Called, Colour is :: Green, Sender is :: Signal1
Important Details
Signature of the event is, (object source, Colour e) which helps in passing the relevant information across to the RailwayUser called, We now know the RailwaySignal triggering the notification to the RailwayUser and its Colour value
Event / Delegate has same signature as called method (which is the basis of working of Delegate / function pointers)
For simplification RailwayUser is a non abstract class
Event is executed using Notify() method inside the RailwaySignal, we are calling it artificially using TestNotification() inside RailwayUser just for demo purpose, but ideally it shall be internally triggered and shall pass on current state like Colour
Pre-defined delegates like Func, Action are quite often used for similar notification mechanism, They internally works using similar mechanism, though declaring an explicit event which is internally a delegate is a well defined pattern, especially for the Ui controls
Standard events exposed by the .Net framework have the signature object sender, EventArgs e, where EventArgs can wrap all information from Event executor (RailwaySignal) to Event receiver (RailwayUser)
It seem like a Observer pattern.You can pass SubClass which inherit from RailwayUser object instance into RailwaySignal class
Your RailwayUser class need create public abstract void Notice(Colour state) method.
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
Driver class can inherit RailwayUser class then override Notice method.
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine($"Driver see the {state.ToString()}");
}
}
There are
List<RailwayUser> _watches contain observable object
use SubScript(RailwayUser user) subscription user on _watches List.
RailwayUser Notify() to invoke all your observable Notify method.
look like this.
public class RailwaySignal
{
private List<RailwayUser> _watches;
public Colour Stata { get; set; }
public RailwaySignal()
{
_watches = new List<RailwayUser>();
}
public void SubScript(RailwayUser user)
{
_watches.Add(user);
}
public void Notify()
{
foreach (RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(Stata);
}
}
}
sample:https://dotnetfiddle.net/GcdGMy
You can also use event to pass method into RailwaySignal then invoke Notify method.
public enum Colour
{
Green,
Red,
Disable
}
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Driver see the "+ state.ToString());
}
}
public class Controller : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Controller see the " + state.ToString());
}
}
public class RailwaySignal
{
public delegate void NoticeEvent(Colour state);
public event NoticeEvent Notifys;
public Colour Stata { get; set; }
public void Notify()
{
if (Notifys != null)
{
Notifys(Stata);
}
}
}
use like this.
RailwaySignal railway = new RailwaySignal() { Stata = Colour.Green};
railway.Notifys += new Driver().Notice;
railway.Notifys += new Controller().Notice;
railway.Notify();
sample : https://dotnetfiddle.net/GcdGMy

Event handler inheritance

I have a parent class that is firing an event to derived classes. The problem is that the event handler is alway null.
Class Plugin()
{
public delegate void BufferReadyHandler(string str);
public event BufferReadyHandler OnBufferReady;
public ClassPlugin(eGuiType _guyType)
{
GuiType = _guyType;
}
protected void Sp_DataReceived_Parent(object sender, SerialDataReceivedEventArgs e)
{
strCommonBuffer += serial.ReadExisting();
if (strCommonBuffer.Contains("\r\n"))
{
if (OnBufferReady != null) <<-------NULL
OnBufferReady(strCommonBuffer);
strCommonBuffer = string.Empty;
}
}
}
then there are some derived classes that are linked to that event:
class ClassIO : ClassPlugin
{
public ClassIO(eGuiType _guyType) : base(_guyType)
{
...
OnBufferReady += ClassIO_OnBufferReady;
}
private void ClassIO_OnBufferReady(string str)
{
...
}
}
the problem is that the OnBufferReady event in the parent class is alway null and therefore never fired.
Thanks for any help.
I might be wrong but have you thought about making the event static?
public delegate void BufferReadyHandler(string str);
public static event BufferReadyHandler OnBufferReady;
I am not sure why you are having this problem, I suspect it has something to do with the code you have not shown us. However in this situation I would not have the child subscribe to the event at all, instead make a protected method that raises the event that the child can override.
Here is how I would implement the class.
public class BufferReadyEventArgs : EventArgs
{
public BufferReadyEventArgs(string commonBuffer)
{
CommonBuffer = commonBuffer;
}
public string CommonBuffer {get; private set;}
}
Class Plugin()
{
public event EventHandler<BufferReadyEventArgs> OnBufferReady;
public ClassPlugin(eGuiType _guyType)
{
GuiType = _guyType;
}
protected void Sp_DataReceived_Parent(object sender, SerialDataReceivedEventArgs e)
{
strCommonBuffer += serial.ReadExisting();
if (strCommonBuffer.Contains("\r\n"))
{
RaiseOnBufferReady(strCommonBuffer);
strCommonBuffer = string.Empty;
}
}
protected virtual void RaiseOnBufferReady(string commonBuffer)
{
var temp = OnBufferReady;
if(temp != null)
temp(this, new BufferReadyEventArgs(commonBuffer));
}
}
class ClassIO : ClassPlugin
{
public ClassIO(eGuiType _guyType) : base(_guyType)
{
...
}
protected override void RaiseOnBufferReady(string commonBuffer)
{
base.RaiseOnBufferReady(commonBuffer);
...
}
}
Here is a working example based on your code:
using System;
using System.Collections.Generic;
public class MyClass
{
public static void Main()
{
ClassIO c = new ClassIO();
c.DataReceived();
Console.ReadLine();
}
}
public class ClassPlugin
{
public delegate void BufferReadyHandler(string str);
public event BufferReadyHandler OnBufferReady;
public ClassPlugin()
{
}
public void DataReceived()
{
if (OnBufferReady != null) {
OnBufferReady("Calling OnBufferReady");
}
}
}
public class ClassIO : ClassPlugin
{
public ClassIO() : base()
{
OnBufferReady += ClassIO_OnBufferReady;
}
private void ClassIO_OnBufferReady(string str)
{
Console.WriteLine("Inside ClassIO_OnBufferReady");
}
}
I don't understand why you would like to work with events in the first place for communication between parent and derived class.
If you need this communication, you would be better of with an (abstract) method in your base class that you implement in your derived classes.
If you need communication to all instances of derived types, you should look into composition instead of inheritance. Make some sort of manager instance that holds references to a list of instances of that base type and invokes a certain method on each of them in case of an 'event'.

How to subscribe to an event fired from any Instance/Object of type X

i'm currently working on a small game-engine and i'm kinda stuck on finding the right pattern.
First here are some code snippets to understand what i'm trying to achieve :
The Unit : the ability-caster/target ( throw the ability or receive it)
public class Unit
{
public string Name { get; set; } // Unit name
public List<BaseAbility> MyAbilities{get; set;} // abilities that an unit has.
}
The Ability :
public abstract class BaseAbility
{
public string Name {get; set;} // ability name
public Unit Caster { get; set; } // ability caster (owner)
public List<BaseEffect> EffectList {get; set;} // effects that an ability possess
// apply all effects that this ability has on the selected target
public virtual void DoAbility(Unit target)
{
foreach (BaseEffect eff in this.EffectList)
{
eff.CalculateEffect(target);
}
}
// here we gonna subscribe to some events
public abstract void initListeners();
}
The Effect :
public abstract class BaseEffect
{
public BaseAbility Owner { get; set; } // each effect belong to an ability
protected string Name { get; set; } // effect name
public EventDispatcher Event { get; set; } // the event to dispatched when this effect is called or used
//apply effect on target
public virtual void CalculateEffect(Unit target)
{
// Do Stuffs here like damage and poison etc etc
this.Event.DispatchMyEvent();
}
}
the Event Dispatcher :
public class EventDispatcher
{
private object mySender;
private EffectEventArgs myArgument;
public delegate void EventHandler(object sender, EffectEventArgs argument);
public event EventHandler OnMyEvent;
public EventDispatcher(object sender, EffectEventArgs argument)
{
this.mySender = sender;
this.myArgument = argument;
}
public void DispatchMyEvent()
{
if (OnMyEvent != null)
{
OnMyEvent(this.mySender, this.myArgument);
Debug.WriteLine("event has been raised");
}
}
}
The Effect Event Arguments :
public class EffectEventArgs : EventArgs
{
private Unit target; // target affected by effect
// not sure if we even need this EffectEventArgs ?!
public EffectEventArgs(Unit unit)
{
this.target = unit;
}
}
Now i'm gonna create 2 effects and 2 abilities and 2 units for the simulation :
Fire Effect :
public class FireEffect : BaseEffect
{
public FireEffect(BaseAbility effectOwner)
{
this.Owner = effectOwner;
this.Name = "Fire";
}
public override void CalculateEffect(Unit target)
{
// set the event here (to get the target as argument for the event)
this.Event = new EventDispatcher(this.Owner, new EffectEventArgs(target));
base.CalculateEffect(target);
}
}
Wind Effect : same as fire effect but with a different name and event and apply also a different Effect.
Fire Ball ability :
public class FireBall : BaseAbility
{
public FireBall(Unit caster)
{
this.Name = "FireBall";
this.Caster = caster;
this.EffectList = new List<BaseEffect>();
this.EffectList.Add(new FireEffect(this)); // fire ball ability has "FireEffect" as effect
}
public override void DoEffect(Unit target)
{
base.DoEffect(target);
}
}
Two Units , let´s say :
"Dragon" as Unit => has FireBall (ability) and gonna hit =>
"shaolin master of the wind " as Unit has a passiveAbility called "WindFury"
WindFury :
public class PassiveAbility : BaseAbility
{
public PassiveAbility(Unit caster)
{
this.Name = "WindFury";
this.Caster = caster;
this.EffectList = new List<BaseEffect>();
this.EffectList.Add(new WindEffect(this));
this.initListeners(); // subscribe to events (aka add listeners)
}
public override void DoEffect(Unit target)
{
base.DoEffect(target);
}
// THE MAIN PROBLEM :
public override void initListeners()
{
// here i need to subscribe to an event fired from any Instance/Object of type FireEffect so that the targeted Unit
// can react to the Unit caster (kind of counter attack)
// to resume this , it should follows this logic :
// if any Ability has The effect(FireEffect => FireBall for example) was casted
// on this Unit thats possess this passive, react to it and counter with (WindEffect)
// otherwise put : when our dragon or Ryu or even Sasuke throw an ability with
// a FireEffect on our friend "Shaolin" ,he should react to it
// and send the attacker flying with a powerfull WindEffect
}
public void CallBack(object sender, EffectEventArgs e)
{
BaseAbility ab = (BaseAbility)sender;
this.DoEffect(ab.UnitCaster); // hit the unit caster back (revenge)
}
}
Notice please that we can have so many dragons and Shaolins in this game or simulation and any passive holder (unit) should only react to the attacker (unit) etc.
Edit : (Recap of the main problem)
How to subscribe via PassiveAbility.initListeners() to an event fired from any instance of type FireEffect (an ability that use fireEffect) ???
Many thanks in advance for your support and contribution.
Youness
Add a static EventDispatcher to FireEffect and subscribe to that.
public class FireEffect : BaseEffect
{
public static EventDispatcher FireEffectEventDispatcher = new EventDispatcher(null,null);
public FireEffect(BaseAbility effectOwner)
{
this.Owner = effectOwner;
this.Name = "Fire";
}
public override void CalculateEffect(Unit target)
{
// set the event here (to get the target as argument for the event)
FireEffectEventDispatcher.mySender = Owner;
FireEffectEventDispatcher.myArgument = new EffectEventArgs(target);
// Fire the event
FireEffectEventDispatcher.DispatchMyEvent();
//If you still want the base effect firing.
base.CalculateEffect(target);
}
}
To make this work, you'll also need to change your EventDispatcher to let you set the sender / argument after construction (not being able to doesn't make any sense anyway - it's a useless restriction).
I would recommend redoing (or removing) your EventDispatcher implementation, however. It makes no sense to set your event parameters in the constructor when they can more reasonably be set during event triggering.

Subscribing inherited methods to events in a constructor, then calling that constructor in an inherited class

I seem to have a problem in C# with constructors, inheritance and event subscription.
Consider the following C# program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventTest
{
public class Widget
{
public delegate void MyEvent();
public event MyEvent myEvent;
public void SetEvent()
{
myEvent();
}
}
public class Base
{
Widget myWidget;
protected Base() { }
protected Base(Widget awidget)
{
myWidget = awidget;
myWidget.myEvent += myEvent;
}
public void myEvent() { }
}
public class Derived : Base
{
public Derived(Widget awidget) : base(awidget) { }
new public void myEvent()
{
System.Console.WriteLine("The event was fired, and this text is the response!");
}
}
class Program
{
static void Main(string[] args)
{
Widget myWidget = new Widget();
Derived myDerived = new Derived(myWidget);
myWidget.SetEvent();
}
}
}
What I want is for the text to be displayed. i.e. I want to subscribe an inherited base method to an event in base class, then be able to call the constructor in a subclass, and get the subclasses' event method to get called instead of the base classes' when that event is fired.
Is there any way to do this?
You need to set the method virtual :
public class Base
{...
public virtual void myEvent() { }
And override it
public class Derived : Base
{
...
public override void myEvent()
{
System.Console.WriteLine("The event was fired, and this text is the response!");
}
}
new public void myEvent()
This creates a new event. You don't want that. Make the event virtual in the base class and use override instead of new here.
Mark the base class method as virtual and your problem will be solved.
public virtual void myEvent() { }

Categories

Resources