4

I am learning about C# event implementation on classes.

I have example case:

There is a Sport and City classes inherit from Car class. Car class has base method call OnBuy that inherited by Sport and City classes. Inside OnBuy method, a event handler has beed assigned to Event Buy.

There is also a Service or Class called LicenseService, that generate license number every time has been bought.

I implemented event driven programming in this case. Here is my git sample:

https://github.com/adityosnrost/CSharpLearningEvent

Questions:

  1. Is this the right way to use Event on C# ?

  2. if this is a right one. Can I override OnBuy method into each childs ? and What can I do if override is available ?

  3. What can I do to make it better from this sample ?

Thank you

class Program
{
    static void Main(string[] args)
    {
        Car car = new Car();
        Sport sport = new Sport();
        City city = new City();

        //car.Name();
        //sport.Name();
        //city.Name();

        //Console.ReadLine();

        LicenseService ls = new LicenseService();

        city.Buy += ls.GenerateLicense;

        city.OnBuy();

        Console.ReadLine();
    }
}

internal class Car
{
    internal virtual void Name()
    {
        Console.WriteLine("Car");
    }

    internal event EventHandler Buy;

    internal virtual void OnBuy()
    {
        EventHandler handler = Buy;
        if (null != handler)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

internal class Sport: Car
{
    internal override void Name()
    {
        Console.WriteLine("Sport");
    }
}

internal class City: Car
{
    internal override void Name()
    {
        Console.WriteLine("City");
    }
}

internal class LicenseService
{
    internal void GenerateLicense(object sender, EventArgs args)
    {
        Random rnd = new Random();

        string carType = sender.GetType().ToString();

        string licenseNumber = "";

        for(int i = 0; i < 5; i++)
        {
            licenseNumber += rnd.Next(0, 9).ToString();
        }

        Console.WriteLine("{1} Car has been bought, this is the license number: {0}", licenseNumber, carType);
    } 
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
Adityo Setyonugroho
  • 877
  • 1
  • 11
  • 29

6 Answers6

5

If I was making this program, I would make following changes,

  • Signature of Event Buy

first it was having two parameters Object and EventArgs where you only need Car in handler method (and also Random discussed below why).

  • I would pass LicenseService in constructor of Child, and register (subscribe) Event in constructor only. that would be more cleaner way.
  • Would make a string member CarName in parent class, so every child can use it anywhere they want.
  • One more thing which i haven't done in this code, I would never name an event Buy, instead I would name it Bought .
  • (This is specific to this scenario only) In your code, inside GenerateLicense() you are creating new object of Random every time. Thus If your two calls for that method are within no time , It will generate same Random number for both calls. Why? see this Question - or you can try below sample code by yourself. So I would pass Already created object of Random in GenerateLicense(). So Random will be common for every call of that method.

Sample code to explain Random number's behavior

        //as object of Random numbers are different,
        //they will generate same numbers
        Random r1 = new Random();
        for(int i = 0; i < 5; i++)
            Console.WriteLine(r1.Next(0, 9));
        Random r2 = new Random();
        for(int i = 0; i < 5; i++)
            Console.WriteLine(r2.Next(0, 9));

Update

  • As suggested by Mrinal Kamboj (in comments below), we should not make Events exposed to external code. Adding his comment too in this answer

Two points, EventHandler Buy cannot be allowed to be directly accessed outside it shall be private, since anyone otherwise can set that to null and all subscription is gone. It needs an Event Accessor, so that event can be accessed using += and -= operators and there itself you make it thread safe for multiple subscribers, else there will be race condition, check a simple example

following is code,

your class structure:

internal delegate void EventHandler(Car car, Random rnd);
internal class Car
{
    internal string CarName;
    internal virtual void SetName()
    {
        this.CarName = "car";
    }

    //Edit : As Mrinal Kamboj suggested in comments below
    //here keeping event Buy as private will prevent it to be used from external code
    private event EventHandler Buy;
    //while having EventAccessros internal (or public) will expose the way to subscribe/unsubscribe it
    internal event EventHandler BuyAccessor
    {
        add 
        {
            lock (this)
            {
                Buy += value;
            }
        }
        remove
        {
            lock (this)
            {
                Buy -= value;
            }
        }
    }

    internal virtual void OnBuy(Random rnd)
    {
        if (Buy != null)
            Buy(this, rnd);
    }
}

internal class Sport: Car
{
    LicenseService m_ls;
    internal Sport(LicenseService ls)
    {
        this.m_ls = ls;
        this.BuyAccessor += ls.GenerateLicense;
        SetName();
    }

    internal override void SetName()
    {
        this.CarName = "Sport";
    }
}

internal class City: Car
{
    LicenseService m_ls;
    internal City(LicenseService ls)
    {
        this.m_ls = ls;
        this.BuyAccessor += ls.GenerateLicense;
        SetName();
    }
    internal override void SetName()
    {
        this.CarName = "City";
    }
}

LicenseService class

internal class LicenseService
{
    internal void GenerateLicense(Car sender, Random rnd)
    {
        string carName = sender.CarName;
        string licenseNumber = "";
        for(int i = 0; i < 5; i++)
        {
            licenseNumber += rnd.Next(0, 9).ToString();
        }
        Console.WriteLine("{1} Car has been bought, this is the license number: {0}", licenseNumber, carName);
    } 
}

and calling the flow

static void Main(string[] args)
{
    Random rnd = new Random();
    LicenseService ls = new LicenseService();
    Sport sport = new Sport(ls);
    City city = new City(ls);

    city.OnBuy(rnd);
    sport.OnBuy(rnd);

    Console.ReadLine();
}
Amit
  • 1,821
  • 1
  • 17
  • 30
  • 1
    What happens when same `Car` object is shared across and one them sets the event as null, even other subscribers lose their subscription (all gone). So where's the event accessor, there shall never be direct access to the Event object, also for multi threaded clients, `Event` is not thread safe, which can lead to corruption / race condition – Mrinal Kamboj May 17 '18 at 07:10
  • @MrinalKamboj ,Even after I understand it partially, I'm afraid that i m not getting your points perfectly. can you put update/edit it in this answer about what you are saying and how to fix it? – Amit May 17 '18 at 07:13
  • Two points, `EventHandler Buy` cannot be allowed to be directly accessed outside it shall be private, since anyone otherwise can set that to null and all subscription is gone. It needs an Event Accessor, so that event can be accessed using `+=` and `-=` operators and there itself you make it thread safe for multiple subscribers, else there will be race condition, check a simple example http://csharpindepth.com/Articles/Chapter2/Events.aspx – Mrinal Kamboj May 17 '18 at 07:17
  • Please make corrections, I have up-voted your answer, since this is the most decent attempt to modify the OP's program in the correct direction – Mrinal Kamboj May 17 '18 at 07:19
  • @MrinalKamboj Thanks.. it seems informative. yes, I m already on it. First let me understand the whole concept and try it my self. and first of all "lunch"! – Amit May 17 '18 at 07:26
  • @MrinalKamboj can you check it now? and let me know if it is okay or still need correction? – Amit May 17 '18 at 07:53
  • Event design looks fine with addition of the accessor – Mrinal Kamboj May 17 '18 at 07:55
  • Hi, thank you so much on your amazing collaboration on this question. I am so happy being helped by both of you and community. Please be patient with me while I am understanding about this :). I have some following question regarding to your solution. while you are adding BuyAccessor to prevent Buy EventHandler accessed by other, why you are not using BuyAccessor to register GenerateLicense instead of using Buy ? – Adityo Setyonugroho May 19 '18 at 11:12
  • And also, while delegate usually used by event in C#. I found that you are using delegate with name EventHandler and parameter Car. I am also in path on understanding delegate. Is GenerateLicense method is an implementation of delegate declaration ? Thanks – Adityo Setyonugroho May 19 '18 at 11:14
  • @AdityoSetyonugroho I can see curiosity in you. Good for you. Why not subscribing GenerateLicense Using BuyAccessor? I must say, you have sharp eyes, that was left unchanged in my last edit (in fact as Buy is private, it will not be even available in child class directly) . it should by BuyAccessor not Buy. I will edit it. And lastly, As our event Buy is of EventHandler delegate type. every listener (handler) method of this event must follow signature of that delegate. – Amit May 19 '18 at 11:38
  • 1
    @Amit Thank you so much for your amazing reply. Regarding to delegate that you were assigned to EventHandler. So, from my understanding that every EventHandler type will always sending an object type Car that you were set on delegation on top in this namespace. Is this correct ? Thank you – Adityo Setyonugroho May 19 '18 at 11:48
  • @AdityoSetyonugroho exactly, every EventHandler typed (here event buy) will always send an object of Car, and as it is sending an object of car as argument parameter, Every handler (which listen this event) method will always accept an object of Car. hope it clears your basics and you practice more on that. you are welcome – Amit May 19 '18 at 11:58
  • 1
    @Amit Wow, thank you so much. It is really helpful information for me to understand about them. I will practice more on implementing this feature of C#. Thank you so much :) – Adityo Setyonugroho May 19 '18 at 12:03
  • @Amit I found a bug or unexpected behavior. When I ran the code, both license for Sport and City are the same. But, if I added a pause or readkey or readline between both OnBuy, It showed us different License number for City and Sport. Any Idea ? or the license object should be instantiate inside both of them not in main program ? – Adityo Setyonugroho May 19 '18 at 13:33
  • @AdityoSetyonugroho Yes. That is expected behavior. It is because we have created different copies of Random if you keep same object of it in all calls. It does create different values every time. See this Question . However I'm updating answer accordingly – Amit May 19 '18 at 15:01
  • @Amit Thanks a lot. I got it know, that the behavior of Random if it is just a tiny different in time of usage. I have update my git for that. – Adityo Setyonugroho May 19 '18 at 15:57
3

It's a bit of a long answer, but I will address your example first and then move on to some general tips. Bare in mind all code is pseudo code, it will not compile without syntax adjustments.

First of all, your logical structure does not make sense, which is why it might be hard for you to pinpoint if this is correct or not.

For instance in real world, you do not address a car to buy it, you address a shop or a service that sells them. You only address a car to drive, or use other functionality that it offers. The car does not assign itself a license. Finally, a purchase is a process that is generally linear (and can be expressed by a method, without triggers) if you take a basic sellers/buyer example. So when you call shop.BuyCar( sportsCar ) all of the purchase logic can be called from the buy method.

Class Shop{ 

    public Car BuyCar( carWithoutLicense ){
        //Purchase logic
        LicenseService.AssignLicense( carWithoutLicense ).
        return carWithoutLicense.
    }
}
//A person would call this method, no the car

A better example of correctly utilized event would be one of the alert lights on the front panel of a car, because it is there to notify the driver of something he/she might want to react to. For instance: a check engine light.

class Car {
   Bool CheckEngingLightIsOn = false;
   public void Ride( ... ){
    if( enginge.faultDetected == true ){
       TurnOnCheckEngineAlert( );
    }
   }
   public void TurnOnCheckEngineAlert( ){
      CheckEngingLightIsOn = true;
      if( CheckEngineLightSwitch != null ){
         CheckEngineLightSwitch.Invoke( ... )
      }
   }
}

class Driver {
   public Driver( Car car ){
      this.car = car;
      if( driverState != Drunk ){
       car.CheckEngineLightSwitch = TakeAction;
      }
   }
   public Drive( ){
      car.Ride( );
   }
   public void TakeAction( Car car, EventArgs e ){
      //get out, open the hood, check the engine...
      if( car.CheckEngingLightIsOn == true ){ "Light turned on
        //Check Engine
      }else{
        //continue driving
      }
   }
}

Without getting too deep into abstraction, notice the chain of events:

  • Driver drives a car and does not worry about other things (such as that check engine light) until they happen.
  • If Car detects a fault, it turns on the check engine light and there are event handlers on that event (subscribers) the car triggers the event.
  • The event fires, but the driver has to be subscribed to it to notice the change.
  • ONLY if the driver is subscribed to that event (in this case, if not drunk) he will take action, based on that event.

This example is fundamentally different to your example, because:

  • While driving the car, the driver doesn't need to pay attention to check engine light all the time (even though, he can check it).
  • It IS the standard process of a car to check engine state and represent it on the engine light.
  • Driver and the Car both affect each others further actions and this logic is inefficient if express linearly.

In other words, Buying process is set in stone (paying, license, goods transfer) and skip essential steps cannot be skipped. The process of a car moving itself is not set in stone, because neither the car nor the driver knows what will happen in journey. I.E. Driver might drive to destination without stopping (if no fault develops or if he doesn't notice the light) or the car might force the driver to stop when he notices the check engine light go on (essentially, they both control each other in a sense).


Some general tips about your use-case

In your example You have made a somewhat complex use-case (with incorrectly placed logic), which it will run, but will be structurally incorrect (and bad logical structure tends to lead to human mistakes when designing further logic).

First thing you should look at is what logic is relevant to each object. Does an event/method in your object represent something that your object does (i.e. functionality that an object performs itself) or something that affects your object but the object itself does not do anything in process? For instance: a car does "riding" on it's own (even if start of this process and all of its parameters, such as speed or direction are controlled by the driver); assigning license to a car happens completely outside of the car structure and the car only get an attribute changed (license) in process.

This distinction is important because only logic performed by your object is relevant to that object, and by extension, any logic that is performed by another object and only affects your object is irrelevant belongs somewhere else. So Buy definitely does not belong in the car and Ride (the process of moving) belongs to the car.

Secondly, your naming will go a long way to help you understand this topic. Methods represent actions and should be named as such (Shop.BuyCar, Car.Ride, Driver.Drive ), events represent reaction triggers ( Car.CheckEngineLightSwitch) and event handlers represent reactions to an action (a reaction is still an action, so no special naming is needed, but you can name to to make a distinction between an action and a reaction).

Zero
  • 1,562
  • 1
  • 13
  • 29
  • Thank you for your amazing detail explanation and detail on using it in reality. I've learn so much and understand on daily basis sequences implementation of this scenario. Your amazing tips will help me to make an example more easy to understand. – Adityo Setyonugroho May 19 '18 at 12:00
  • Upvoted because your explanation actually bothers to care about good OOP class design. A hallmark of quickly-developed code in the corporate/real world is shortcuts such as writing a function in the class that you happen to have the code file open for, but which primarily operates on some other class(es), violating the basic principle of encapsulation and saddling you with massive technical debt in the form of heinous code coupling. Thank you for bothering to provide a full and eloquent answer, even for such a simple and almost obviously homework-type question. – dodexahedron May 25 '18 at 05:27
1

Nope.

Events should not be called by anyone other than the class that owns the event (or, if you know what you're doing, classes that inherit from the class that owns the event - a d even then, they should probably pay attention to the original implementation to avoid subtle issues)

Basically, a subscription to an event is a promise that, when a given thing happens, you'll call the functions you've fed to it from subscribers. The event itself is simple the code construct that allows you to make that subscription, without having to know or implement the intricacies of multicast function pointer invocation.

Otherwise, you're just calling a function and may as well not bother with an event.

Events are essentially intentional code injection - they allow you to make some other class execute arbitrary code that you wrote, when they do a thing.

dodexahedron
  • 4,584
  • 1
  • 25
  • 37
  • In OP's design also Event is internally invoked in the `OnBuy()`, it is only for sake of sample its created and exposed. Also event is not any arbitrary code, it's a function pointer, which is based on publisher subscriber pattern, where event exposing class publish something and subscriber consumes it and process in the associated method – Mrinal Kamboj May 17 '18 at 07:13
  • The purpose of a function pointer is to be able to pass execution of arbitrary code around. Not really sure the purpose of this comment. Arbitrary doesn't mean dumb, unstructured, or random. It just means..well...whatever you wanted to pass, so long as it matches the type-safe signature of the delegate (which is how they're different from function pointers, which are very much not type-safe). – dodexahedron May 25 '18 at 04:50
1

I recommend you to inject licence service to car, and call generate licence function when buy is called,

using System;

namespace ConsoleApp1.Test
{

    class Program
    {
        static void Maintest(string[] args)
        {
            ILicenseService licenseService = new LicenseService();

            Sport sport = new Sport(licenseService);
            City city = new City(licenseService);

            //car.Name();
            //sport.Name();
            //city.Name();

            //Console.ReadLine();            

            city.OnBuy();

            Console.ReadLine();
        }
    }

    internal abstract class Car
    {
        protected readonly ILicenseService licenseService;

        public Car(ILicenseService _licenseService)
        {
            licenseService = _licenseService;
        }

        internal virtual void Name()
        {
            Console.WriteLine("Car");
        }

        internal event EventHandler Buy;

        internal virtual void OnBuy()
        {
            // TODO
        }
    }

    internal class Sport : Car
    {
        public Sport(ILicenseService _licenseService) : base(_licenseService)
        {

        }

        internal override void Name()
        {
            Console.WriteLine("Sport");
        }

        internal override void OnBuy()
        {
            licenseService.GenerateLicense(new object());
        }
    }

    internal class City : Car
    {
        public City(ILicenseService _licenseService) : base(_licenseService)
        {

        }

        internal override void Name()
        {
            Console.WriteLine("City");
        }

        internal override void OnBuy()
        {
            licenseService.GenerateLicense(new object());
        }
    }

    internal interface ILicenseService
    {
        void GenerateLicense(object param);
    }

    internal class LicenseService : ILicenseService
    {
        public void GenerateLicense(object param)
        {
            // do your stuff
        }
    }

}
programtreasures
  • 4,250
  • 1
  • 10
  • 29
  • 1
    main motive of this post is OP is trying to implement Events in proper way and here you have eliminated the entier usage of event. – Amit May 17 '18 at 06:47
  • 1
    @Amit Actually OP doesnt need event in his current cenario, OP can design without event. – programtreasures May 17 '18 at 12:58
  • That's one reason why super-simple examples of event patterns are terrible learning samples. Most samples around the internet (even here, on SO) may as well just be function calls, and most aspiring software engineers can see right through that. The hard part is imparting the concept of inversion of control, which is ultimately actually what an event provides, in a first-class, type-safe, language-supported construct that works very well with the publisher-subscriber paradigm – dodexahedron May 25 '18 at 04:57
  • 1
    @dodexahedron correct..!! this type of super-simple examples cant imaging things. when thinking in event driven or pub sub paradigm we thing about observer pattern. – programtreasures May 25 '18 at 05:05
  • So many applications that hot-loop or poll over objects/collections would benefit so much by using event patterns. All applications break down to some sort of state machine. If that state machine can simply run ONCE per state change of the entire system (threading of course makes this more fun), that's literally the most efficient way to handle things. Nobody cares about anything unless state has changed, so all other methods are just silly. Once this concept clicks for a developer, they immediately level up. Render loops for games/UIs don't count because those are necessarily loops. – dodexahedron May 25 '18 at 05:12
  • As a character-limit-exceeding addition... I, personally, dislike the design guideline that event delegates either be the EventHandler class or follow the `public void foo(object sender, EventArgs e)` pattern. It kind of reduces the usefulness and static analysis advantages conferred by specific type-safe delegates for events. Sure, it's easier and very standardized, across libraries, but that's very much not an excuse for a language/environment with type safety and things like intellisense. It actually tends to result in several lines more code, if we follow that guideline, vs if we don't. – dodexahedron May 25 '18 at 05:21
1

1) Is this the right way to use Event on C# ?

Not completely. The OnBuy should be a protected virtual method. That also excludes you calling it from the Main() method.

More common would be to call someCar.Buy() and then Buy() wil trigger OnBuy().

2) if this is a right one. Can I override OnBuy method into each childs ? and What can I do if override is available ?

Yes, you can override it See that as a more effective way of subscribing to yourself (which would be the alternative).

You can do anything needed when a specific type of Car is bought. But do alwas call base.OnBuy()

3) What can I do to make it better from this sample ?

The CreateLicense just doesn't sound like a good candidate for an event, it would be more of a business rule called by a CarDealer.

Following modern design rules, a Car would be a rather passive entity object (Anemic Domain Model).

Events would usually be used to tell other components "I have changed, do your thing", not to perform essential actions on self.

H H
  • 263,252
  • 30
  • 330
  • 514
bommelding
  • 2,969
  • 9
  • 14
  • There's no point making `OnBuy` protected, that's just a dummy method to invoke invoke event internally, in reality this would be an internal invocation based on some logic and ideally Event object shall be private, so `OnBuy` in child class can only do `base.OnBuy()`, which is of little use – Mrinal Kamboj May 17 '18 at 07:37
  • 1
    The design guideline is very much to make it protected. With good reason: Encapsulation. – bommelding May 17 '18 at 07:38
  • Which design guideline, that can only be done, it you do Buy event implementation outside base class, so that child classes can access the event at will, not rely on `base.OnBuy`. Btw I have not downvoted, I don't down vote for difference in understanding – Mrinal Kamboj May 17 '18 at 08:17
  • 2
    Er... The .Net design guidelines? The event invocation functions are recommended to always be either protected or private (generally protected, unless it's a sealed class, to allow less pain in inheritance). This is not a secret and a google (or bing, since this is .net, after all) search of ".net event design pattern" returns plenty of results that make this a very made point. A difference in understanding doesn't excuse a misuse of the technology that amounts to encapsulation-breaking or redundant function calls. Events are a specific and well-defined solution for a specific, yet broad, use. – dodexahedron May 25 '18 at 05:03
1

Following would be my preferred design:

class Program
{
    static void Main(string[] args)
    {
        Car car = new Sport();
        car.BuyEvent += License.GenerateLicense;        
        car.OnBuy();
        car = new City();
        car.BuyEvent += License.GenerateLicense;
        car.OnBuy();
    }
}

internal abstract class Car
{
    internal abstract void Name();

    protected abstract event EventHandler Buy;

    public abstract event EventHandler BuyEvent;

    public abstract void OnBuy();   
}

internal class Sport : Car
{
    internal override void Name()
    {
        Console.WriteLine("Sport Car");
    }

    protected override event EventHandler Buy;

    public override event EventHandler BuyEvent
    {
        add
        {
            lock (this)
            {
                Buy += value;
            }
        }

        remove
        {
            lock (this)
            {
                Buy -= value;
            }
        }
    }

    public override void OnBuy()
    {
        if (Buy != null)
        {
            Buy(this, EventArgs.Empty);
        }
    }

}

internal class City : Car
{
    internal override void Name()
    {
        Console.WriteLine("City Car");
    }

    protected override event EventHandler Buy;

    public override event EventHandler BuyEvent
    {
        add
        {
            lock (this)
            {
                Buy += value;
            }
        }

        remove
        {
            lock (this)
            {
                Buy -= value;
            }
        }
    }

    public override void OnBuy()
    {
        if (Buy != null)
        {
            Buy(this, EventArgs.Empty);
        }
    }
}

internal static class License
{

    public static void GenerateLicense(object sender, EventArgs args)
    {
        Random rnd = new Random();

        string carType = sender.GetType().Name;

        string licenseNumber = "";

        for (int i = 0; i < 5; i++)
        {
            licenseNumber += rnd.Next(0, 9).ToString();
        }

        Console.WriteLine($"{carType} Car has been bought, this is the license number: {licenseNumber}");
    }

}

Important points:

  1. Make the Car base class abstract, it shall only be derived and used like City / Sport Car
  2. Add event add / remove accessor in each of the child class for custom processing of the event. Make the accessor thread safe for shared client access
  3. Make License - GenerateLicense as either static, as there's no state / data in the class or integrate them too with the City / Sport class implementation by having another base abstract method
  4. Abstract class Car shall be injected with Sport \ City objects at the runtime and shall be used for event subscription via base class Car object
Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
  • Thank you so much on your solution. If it is alright, I want to ask some questions. I would like to ask more about on using EventArgs args. Some tutorial and sample just did not take any change on this parameter. What is it actually ? What kind of parameter that we can do with it ? Thanks – Adityo Setyonugroho May 19 '18 at 11:26
  • @AdityoSetyonugroho you can pass any type of object (even primitive like int, string) as an object (here first parameter of event, More interesting part is second parameter, it is of type `System.EventArgs`. normally if event requires to pass it but u don't have anything particular to be sent (as you have already sent proper information as first parameter) you can simply pass `EventArgs.Empty` but to use it more productive, you can create a class which inherit the EventArgs class and then pass object of that class as second argument – Amit May 19 '18 at 12:14
  • @AdityoSetyonugroho it can simply understood. You need to pass Object of EventArgs in second parameter, as we cast an object child class with type of parent class easily. Any class which inherit EventArgs class, we can pass object of that class. So as per requirement you need to pass in event (which listeners will have as arguments) you can create a class and that class must inherit EventArgs. So load information in that object and pass it in event. See this [article](https://msdn.microsoft.com/en-us/library/system.eventargs(v=vs.110).aspx) – Amit May 19 '18 at 12:19
  • @Amit Thank you so much on your detail explanation of EventArgs. So, it is just like giving a default value to sent every time the event is triggered, right ? The class that inherit EventArgs is just holding values that needed every time event is being called. Amazing, so much thing can be used over event implementation. – Adityo Setyonugroho May 19 '18 at 12:41
  • Hi @Mrinal Kamboj, I found the same bug or unexpected behavior on this sample also. Like what I commented on Amit post, both Sport and City license are the same. Any idea how can this happened ? Thanks – Adityo Setyonugroho May 19 '18 at 13:35
  • This is because of the faulty design of the Licensing code, you are using Random numbers to generate it and that's a pseudo random number class, which doesn't guarantee uniqueness for such a small set. On adding some delay it may give a different value, but that's never guaranteed. Use a better algorithm, I just used your code, which doesn't use any seed value or at least use `DateTime.Now.Millisecond` value as a seed or have a logic which prevents the duplication by storing the last set of values. – Mrinal Kamboj May 19 '18 at 13:55
  • Regarding `EventArgs` check [this](https://referencesource.microsoft.com/#mscorlib/system/eventargs.cs,9a1496aa60befd10), it is just an empty placeholder, which is mostly to derive further for creating custom events – Mrinal Kamboj May 19 '18 at 13:57
  • Also `rnd.Next(0, 9)` is a very small range it will invariably get repeated when called in quickly called W/o renewing the Random class object – Mrinal Kamboj May 19 '18 at 13:58
  • @MrinalKamboj Thank you for your reply. I have found the problem with Random. I have update the git. Random will using the same number if instantiate in tiny different time. – Adityo Setyonugroho May 19 '18 at 16:00