Wednesday, 10 May 2017

Events 02

previously we created a water tank class that fires an event when it overflows, but it would be nice to for our watertank class to notify our main what level the tank is at when it pases a critical threshold, for example let's use 75% 85% and 95% of the tank capacity. There are multiple ways of doing this one very valid way would be to create an new event for each threshold or to just leverage our current level property, but what we're going to do is leverage the event args class. Let's take a look at the definition of the base eventargs class

using System.Runtime.InteropServices;

namespace System
{
    // Summary: Represents the base class for classes that contain event data, 
    // and provides a value to use for events that do not include event data.
    [ComVisible(true)]
    public class EventArgs
    {
        // Summary: Provides a value to use with events that do not have event data.
        public static readonly EventArgs Empty;

        // Summary:Initializes a new instance of the System.EventArgs class.
        public EventArgs();
    }

}

wow not too much, so let's make a WaterTankEventArgs class that inherits from EventArgs

class WaterTankEventArgs : EventArgs
{
    public enum Threshold { caution = 75, warning = 85, danger = 95 }
    public Threshold Message { get; private set; }
    public int Level { get; private set; }

    public WaterTankEventArgs(int Level, int Capacity)
    {
        this.Level = Level;
           
        if ((double)Threshold.danger / 100.0 * Capacity <= Level)
            Message = Threshold.danger;
        else if ((double)Threshold.warning / 100.0 * Capacity <= Level)
            Message = Threshold.warning;
        else if ((double)Threshold.caution / 100.0 * Capacity <= Level)
            Message = Threshold.caution;
    }
}

we created an enum for our thresholds

  • 75 is caution
  • 85 is warning
  • 95 is danger

we have properties to expose values of interest via message and level then we have a constructor that decides what kind of  message to return to the user.

next we modify our watertank class slightly

class WaterTank
{
    public int Capacity { get; private set; }
    public int CurrentLevel { get; private set; }
    public EventHandler<WaterTankEventArgs> OverflowEventHandler { get; set; }
    public WaterTank(int MaxLiters)
    {
        this.Capacity = MaxLiters;
    }
    public void IncreaseLevel(int Liters)
    {
        CurrentLevel += Liters;

        //if we go over the capacity there's an overflow
        if (OverflowEventHandler != null && Capacity / CurrentLevel * 100 >= 75)
            OverflowEventHandler.Invoke(this,new WaterTankEventArgs(CurrentLevel, Capacity));
    }

}

what we did is in the increase level method we check if there are any subscribers to our event then if the current water level passed our first threshold, if so we raise our event with our newly created WaterTankEventArgs class.

now our main really didn't change all that much if we look at it

class Program
{
    static void Main(string[] args)
    {
        char selection;
        var wt = new WaterTank(50);

        //define and subscribe event
        wt.OverflowEventHandler += (s, e) =>
            Console.WriteLine($"{e.Message.ToString("g")} water level at {e.Level} liters");

        do
        {
            Console.WriteLine("\n1)Add to tank\n2)check current level\n0)exit");

            switch (selection = Console.ReadKey().KeyChar)
            {
                case '1':
                    Console.WriteLine("\nhow many liters to add? ");
                    int l = 0;
                    int.TryParse(Console.ReadLine(), out l);
                    wt.IncreaseLevel(l);
                    break;
                case '2':
                    Console.WriteLine($"\nWater Tank level {wt.CurrentLevel}");
                    break;
                default:
                    Console.WriteLine("No such command");
                    break;
            }

        } while (selection != '0');
    }

}

the only difference is where we define and subscribe to our event, we leverage the event args to give us some feedback about our event.