How to pass abstract method as reference? - c#

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

Related

Problem with events (delegates) and inheritance in Unity 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);
}
}

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'.

Trigger event called by an another class

I want to triggered my event when my UI call my class, life this :
In my UI i call my class ScanClass with a string parameter
ScanMessage scn = new ScanMessage(message);
In my class ScanMessage i made this :
public class ScanMessage
{
public delegate void OnScanMessageReceived(string message);
public ScanMessage()
{
}
public ScanMessage(string message)
{
MessageReceived(message);
}
public event OnScanMessageReceived MessageReceived;
}
And i made this in my FirstViewModel
public FirstViewModel()
{
var scanMessage = new ScanMessage();
scanMessage.MessageReceived += ScanMessage_MessageReceived;
}
private void ScanMessage_MessageReceived(string message)
{
//Do something
}
Well, if i post here, it's because i have a problem and i can't fix it without help !
Thank.
The reason is the instance of ScanMessage is different. UI class has different instance and VM has different instance. Hence raising event on one instance is not being heard in other one.
If all your ScanMessage needs to do is to broadcast event, you can have its singleton instance
public class ScanMessage
{
public delegate void OnScanMessageReceived(string message);
private static ScanMessage _scanMessage = new ScanMessage();
private ScanMessage()
{
}
public static ScanMessage Instance
{
get
{
return _scanMessage;
}
}
public void BroadCastMessage(string message)
{
MessageReceived(message);
}
public event OnScanMessageReceived MessageReceived;
}
And then from UI you can call like
ScanMessage.Instance.BroadCastMessage(message);
and in your VM you can
public FirstViewModel()
{
ScanMessage.Instance.MessageReceived += ScanMessage_MessageReceived;
}
private void ScanMessage_MessageReceived(string message)
{
//Do something
}

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.

Instantiating a delegate method to be used in a class library

I'm building an email-monitoring framework that I'll be using for a handful of users, so I'm building a class library to wrap everything in. I'm instantiating the configuration (sender, subject, last-received, ...) in a static class. Therefore, I have something like this.
public static class MyConfig
{
public static int Sender { get; set; }
// and so on and so forth
public static void BuildMyConfig(string theSender, string theRecipient, ...)
{
Sender = theSender;
// yada yada yada...
}
}
public class Monitoring
{
public delegate void DoSomethingWithEmail(EmailContents theContents);
public void StartMonitoring() {
//When I get an email, I call the method
DoSomethingWithEmail(theEmailWeJustGot);
}
}
Obviously, what we do with the email will be something completely different in each case. What I'm trying to is instantiate that delegate. Where would I do that? The MyConfig class and then invoke it from there as a static method? The instance of the Monitoring class?
An application would look like...
public class SpecificMonitor
{
Monitoring.BuildMyConfig("foo#bar.com", "bar#foo.com", ...);
Monitoring m = new Monitoring();
m.StartMonitoring();
//But where do I build the delegate method???
}
I've gotten compiling errors with every option I've tried so far. I've also tried overriding a method instead of using a delegate, using interfaces... but I think delegation is where it's at.
Thanks in advance!
Consistent with the rest of your design (although I do not necessarily agree that the design is great) you could allow for the callback to be set in the configuration class
public static class MyConfig
{
public static string Sender { get; set; }
public static DoSomethingWithEmail EmailReceivedCallback { get; set; }
public static void BuildMyConfig(string theSender, string theRecipient,
DoSomethingWithEmail callback)
{
Sender = theSender;
EmailReceivedCallback = callback;
}
}
// Make sure you bring the delegate outside of the Monitoring class!
public delegate void DoSomethingWithEmail(string theContents);
When an incoming email is acknowledged by your application you can now pass the email to the callback assigned to the configuration class
public class Monitoring
{
public void StartMonitoring()
{
const string receivedEmail = "New Answer on your SO Question!";
//Invoke the callback assigned to the config class
MyConfig.EmailReceivedCallback(receivedEmail);
}
}
Here is an example of usage
static void Main()
{
MyConfig.BuildMyConfig("...", "...", HandleEmail);
var monitoring = new Monitoring();
monitoring.StartMonitoring();
}
static void HandleEmail(string thecontents)
{
// Sample implementation
Console.WriteLine("Received Email: {0}",thecontents);
}
Define the constructor so that when people instantiate a Monitoring object, they must define the delegate:
public class Monitoring
{
public delegate void DoSomethingWithEmail(EmailContents theContents);
public Monitoring(Delegate DoSomethingWithEmail)
{
this.DoSomethingWithEmail = DoSomethingWithEmail;
}
public void StartMonitoring() {
//When I get an email, I call the method
DoSomethingWithEmail(theEmailWeJustGot);
}
}
Then pass in the delegate you want when you instantiate each Monitoring:
Monitoring m = new Monitoring(delegate(EmailContents theContents)
{
/* Do stuff with theContents here */
});
m.StartMonitoring();

Categories

Resources