Alexia
Alexia

Reputation: 83

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)
    }
}

enter image description here

Upvotes: 1

Views: 315

Answers (2)

Mrinal Kamboj
Mrinal Kamboj

Reputation: 11478

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)

Upvotes: 2

D-Shih
D-Shih

Reputation: 46239

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

  1. List<RailwayUser> _watches contain observable object
  2. use SubScript(RailwayUser user) subscription user on _watches List.
  3. 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

Upvotes: 1

Related Questions