Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Events and Delegates for Beginners

0.00/5 (No votes)
19 Nov 2010 1  
This beginners articles shows how do you create simple delegate, multicast delegate and create your custom events

1. Introduction

Delegate is a reference type just like other object. When you create an object, the memory is allocated for the object on the heap and reference for it is stored in the reference variable, which is in the stack. Consider the below statement:

Organization Org = new Organization("C# Corner", staff1, staff2, staff3, staff4 );

Here, the organization object is created on the Heap memory and a reference to that memory location is stored on the stack identified by the token Org. Like the Org reference, the delegate reference type will refer to the function address. At runtime, the function will be loaded into a code segment of memory like Heap Segment for object created using new keyword. If we take the starting address of the function (first line of translated code) in the code segment and store it in a reference variable, we call that reference variable a Delegate.

2. Declaring a Delegate

Below is the syntax for declaring a delegate:

{scope}delegate {returntype}DelegateName({parameters }); 

In the above syntax:

  • Scope: It is access specification like public, private, etc.,
  • delegate: keyword
  • returntype: Function return type
  • DelegateName: Name of the delegate
  • Parameters: Function parameter names with types

Once delegate is declared, you can create the instance of the delegates. Just think about the class below:

class Publishers {}

The class keyword is used to specify the token Publishers as a class template. Later, you can create the object of template type Publishers. The same holds true for delegates. The above syntax just shows how to declare a delegate. Consider the below example for the syntax:

public delegate int GetTotalDelegate(Staff[] staffs);

In the above declaration, we are told that there is a delegate named GetTotalDelegate, which takes Array of Staff as parameter and returns an integer to the caller. Later, you can create an instance of the delegate type GetTotalDelegate.

3. Creating a Delegate Reference

Now look at the below statement:

GetTotalDelegate Salary_Total = new GetTotalDelegate(Total_Salary );

In the above statement, we created the instance of delegate reference. What reference? Salary_Total. What is the type of Reference? GetTotalDelegate. As you see, you are actually creating an object of type GetTotalDelegate. Now, go ahead and look at the syntax example once again. Got, the clue? Right. As per the example, the compiler will actually create a class of type GetTotalDelegate and accepts any function name (Takes it as address reference) that takes an array of Staff and returns an integer. Here Total_Salary is the name of the function we are passing in and that function takes an Array of Staff and returns an integer.

Boring? OK. Let me walk you through an example.

4. The Staff Class

This class is self-explanatory. It has some field members, a constructor to initialize them and a ToString override. Below is the class:

//001: A class for Staff
public class Staff 
{ 
    //001_1: Member variables 
    private int StaffId; 
    private string StaffName; 
    public int Salary; 
    public int Bonus; 

    //001_2: Constructor for Staff 
    public Staff(int id, string name, int Salary, int bonus) 
    { 
        StaffId = id; 
        StaffName = name; 
        this.Salary = Salary; 
        Bonus = bonus; 
    } 

    //001_3: String representation of staff 
    public override string ToString() 
    { 
        return string.Format("{0} - {1}", StaffName, StaffId); 
    } 
}

5. The Organization Class

This class has Array of staffs who forms the Organization.

  1. First, a delegate is declared. The delegate name is GetTotalDelegate and it takes array of staff as parameter and returns an integer. Below is delegate:
    //002_1: Delegate that Calculates and return the Total
    public delegate int GetTotalDelegate(Staff[] staffs); 
  2. Next, two member variables are placed in this class. One is Array of staff and other one is for Name of the Organization.
    //002_2: Other member variables
    private Staff[] Staffs; 
    private string Org_Name; 
  3. The constructor will initialize the internal members. Constructor code is given below:
    //002_3: Constructor for Organization
    public Organization(string Org_name, params Staff[] staffs) 
    { 
        //002_3.1: Initialize the Staffs Array 
        Staffs = new Staff[staffs.Length]; 
        for(int i=0; i < staffs.Length; i++) 
          Staffs[i] = staffs[i]; 
    
        //002_3.2: Initialize other member variables 
        Org_Name = Org_name ; 
    } 
  4. The Calculate_Total function takes the delegate of type GetTotalDelegate as parameter. Makes a call to the function referred by the delegate and returns the return value of the delegate parameter delegateRef. Note that when we are making a call with delegate, the parameter passed-in is Staff array and as return value integer, the function Calculate_Total returns an integer. Here, we do not bother what is implemented by the function that came as the parameter in the form of delegate. Below is the Function that receives function as parameter (Delegate) and returns an integer:
    //002_4: Function that delegates the work of Calculating Total
    public int Calculate_Total(GetTotalDelegate delegateRef) 
    { 
        return delegateRef(Staffs); 
    } 
  5. The DisplayStaffs function walks through the Staffs array and prints the staff object. Note, the ToString override is called as the Console.WriteLine tries to represent the Staff in string format. Below is the function:
     
    //002_5: Display all Staffs
    public void DisplayStaffs() 
    { 
        foreach(Staff staff in Staffs) 
            Console.WriteLine(staff); 
    } 

    Full Organization class is given below:

    //002: Organization has Staffs for its Operation
    public class Organization 
    { 
        //002_1: Delegate that Calculates and return the Total 
        public delegate int GetTotalDelegate(Staff[] staffs); 
    
        //002_2: Other member variables 
        private Staff[] Staffs; 
        private string Org_Name; 
    
        //002_3: Constructor for Organization 
        public Organization(string Org_name, params Staff[] staffs) 
        { 
            002_3.1: Initialize the Staffs Array 
            Staffs = new Staff[staffs.Length]; 
            for(int i=0; i<staffs.length; staffs[i]="staffs[i];">//002_3.2: Initialize other member variables 
            Org_Name = Org_name ; 
        } 
    
        //002_4: Function that delegates the work of Calculating Total 
        public int Calculate_Total(GetTotalDelegate delegateRef) 
        { 
            return delegateRef(Staffs); 
        } 
    
        //002_5: Display all Staffs 
        public void DisplayStaffs() 
        { 
            foreach(Staff staff in Staffs) 
                Console.WriteLine(staff); 
        }
    }

6. The Calculate Utility Class

If a class has all static functions in it, we call it a utility class. As all the members of the class are static, the clients do not need to create an instance and instead they can directly access the function by using the class name. This class implements two functions. One function calculates Total salary and the other function Calculated Total Bonus. Note these function signatures map the delegate we declared in the Organization class. That is both the functions receive Staff Array as parameter and return an integer. The Organization class delegate is going to use these functions and you will see that sooner. Below is the Utility class [hope no more explanation is required]:

//003: Utility Class for Making Calculation
public class Calculate 
{ 
    //003_1: Helper function to Calculate Total Salary Expense 
    public static int Total_Salary(Staff[] Staffs) 
    { 
        int sum = 0; 
        foreach(Staff staff in Staffs) 
             sum = sum + staff.Salary ; 
        return sum; 
    } 
    
    //003_2: Helper function to Calculate Total Bonus for All Staffs 
    public static int Total_Bonus(Staff[] Staffs) 
    { 
        int sum = 0; 
        foreach(Staff staff in Staffs) 
            sum = sum + staff.Bonus ; 
        return sum; 
    } 
}

7. Delegate Usage

Let us see how the user of the above classes uses the delegate. First instances of four Staff are created.

//Client 001: Create Staffs 
Staff staff1 = new Staff(100, "Mahesh Chand", 100000, 10000); 
Staff staff2 = new Staff(101, "Mike Gold", 80000, 120000); 
Staff staff3 = new Staff(102, "Sundar Lal", 70000, 25000); 
Staff staff4 = new Staff(103, "Praveen", 50000, 27000); 

Next, create the Organization for our example by passing in all the staffs created. The Organization class will copy the array and holds it in the class array member variable Staffs.

//Client 002: Create Organization 
Organization Org = new Organization("C# Corner", staff1, staff2, staff3, staff4 ); 
Org.DisplayStaffs(); 

Next, two delegate instances Salary_Total, Bonus_Total of the same type GetTotalDelegate are created. Note that for the constructor of this delegate, we are passing the function name that matches the delegate by the arguments it receives and type it returns. The compiler by reading the delegate keyword defines a class called GetTotalDelegate. Well, that is behind the scenes of how delegates work. If you are not a beginner who needs extra explanation, download the attached source code and examine the EXE file with ildasm. Look at the internal class GetTotalDelegate that is by the way internal for our Organization class.

//Client 003: Create the Delegates of same type pointing to different function 
Organization.GetTotalDelegate Salary_Total = 
    new Organization.GetTotalDelegate(Calculate.Total_Salary ); 
Organization.GetTotalDelegate Bonus_Total = 
    new Organization.GetTotalDelegate(Calculate.Total_Bonus );

Once the delegates are created, the total expense spent by the Organization is computed by calling the function Calculate_Total of Organization class. This function expects GetTotalDelegate delegate as parameter. GetTotalDelegate is the wrapper class created by the compiler that points to the function in the code segment. Calculate_Total function just makes a call to the function pointed by the wrapper class GetTotalDelegate and returns the Integer. We are making two calls to the Calculate_Total function. First, by sending the Wrapper Class GetTotalDelegate (the delegate type) that points to Total_Salary function of utility class and the second time same type of parameter pointing to different function Total_Bonus. Below is the code:

//Client 004: Now pass these delegates that is pointer to a 
//function wrapped as a class GetTotalDelegate 
to the Organization class member function.
int Total_Org_Expenses; 
Total_Org_Expenses = Org.Calculate_Total(Salary_Total) + Org.Calculate_Total(Bonus_Total); 
Console.WriteLine("Total Expense  : " + Total_Org_Expenses ); 

8. Explore Delegate by ILDASM

Download the demo and examine the EXE using the ILDASM. For beginners [How do I]:

  1. Open the console window using Microsoft Visual Studio => Visual Studio Tools => Visual Studio Command Prompt.
  2. Move to the directory where you extracted the zip file, which has the EXE.
  3. Type ildasm .exe
  4. Examine the to see how the statement public delegate int GetTotalDelegate(Staff[] staffs); converted by the compiler as internal class of type GetTotalDelegate

9. Multicast Delegate

In the previous part, we saw how we can use a simple delegate. In the article, I will explain what is a multicast delegate and how we create and use it. Multicast delegates are the combination of two or more delegates of the same type and they together form a delegate chain. Each participant in the delegate chain should have a void return type. Let us take an example of an Order processing system that makes use of the Multicast delegate. The example I created has an OrderShipment class and that makes use of Multi-cast delegate. First, we will explore the OrderShipment class and then we will move the client code that makes use of the OrderShipment class and Multicast delegate.

10. OrderShipment Class

This class breaks the Order processing into a small group of functions that will match the type single delegate, which is eligible for delegate chaining.

  1. 1) In this class, first a multicast delegate is declared. Actually, it is normal delegate syntax only. Soon, we are going to use this for the purpose delegate chaining. The delegate accepts Order id and customer id as parameter and returns nothing that is void. Please keep in mind that multicast delegate principle work for void return type only. There is no restriction on the Parameters that it receives. Below is the Delegate declaration:
    //001_1: Declare the Multi-cast delegate. Note the return type should be void
    public delegate void OrderProcessingMethods(int OrderId, int CustomerId);
  2. The function split into five parts is actually used for the Order processing. The split functions that will become part of the delegate chaining are shown below:
    //001_2: Implement the Order Processing Functions
    //Processing Function 1
    public void GetShoppingCartItems(int OrderId, int CustomerId)
    {
        Console.WriteLine("(1) GetShoppingCartItems");
        Console.WriteLine("===============================");
        Console.WriteLine("All shopping Cart Items are Collected.");
        Console.WriteLine("Formed a Order with supplied Orderid");
        Console.WriteLine("______________________________________________________");
    }
    
    //Processing Function 2
    public void CalculateOrderPrice(int OrderId, int Customerid)
    {
        Console.WriteLine("(2) CalculateOrderPrice");
        Console.WriteLine("===============================");
        Console.WriteLine
        ("Price of each products collected from the shopping cart summed up");
        Console.WriteLine("Order Price calculated");
        Console.WriteLine("_______________________________________________________");
    }
    
    //Processing Function 3
    public void CalculateDiscount(int OrderId, int Customerid)
    {
        Console.WriteLine("(3) CalculateDiscount");
        Console.WriteLine("===============================");
        Console.WriteLine("Get the Discount amount for the VIP");
        Console.WriteLine("Reduce Order Price");
        Console.WriteLine("______________________________________________________");
    }
    
    //Processing Function 4
    public void AwordFreeGifts(int OrderId, int Customerid)
    {
        Console.WriteLine("(4) AwordFreeGifts");
        Console.WriteLine("===============================");
        Console.WriteLine("Regular Customer. Pick up a gift");
        Console.WriteLine("Place the gift item in the Order for free");
        Console.WriteLine("______________________________________________________");
    }
    
    //Processing Function 5
    public void GetOrderConfirmation(int OrderId, int Customerid)
    {
        Console.WriteLine("(5) GetOrderConfirmation");
        Console.WriteLine("===============================");
        Console.WriteLine("Order confirmation screen shown to the User");
        Console.WriteLine("Order Confirmed");
        Console.WriteLine(".");
    }

    Note, there is nothing more than the call to Console output functions. But, I hope you understand how these functions will be in real world applications.

  3. This class has a Member function that accepts the Multicast delegate as parameter and then makes a call to the multicast delegate. The client will make the delegate chain based on the above five functions and then calls this member function:
    //001_3: Takes a multicase delegate and performs business logic            
    public void ProcessOrderShipment
        (OrderProcessingMethods ProcessToFollow, int Orderid, int Customerid)
    {
        ProcessToFollow(Orderid, Customerid);
    }

Implementation of this class is completed now. Time to go for Delegate chaining.

11. Client Code

The client will process the order shipment differently for three types of customers. The customer types are:

  1. Normal customer
  2. Regular customer who makes purchases monthly once or twice
  3. The VIP customer who has built up a good relation

For normal customer, there is no discount and surprising gifts. The regular customer will have surprising gifts based on the order cost. And VIP customer has a discount as well as gifts. Now lets us go through how the client code makes use of the multicast delegate.

  1. First, the instance of OrderShipment class is created. Code is below:
    //Client 001: Create Ordershipment Object
    OrderShipment deliverorders = new OrderShipment();
  2. Next, the delegate of type OrderProcessingMethods is declared. This delegate variable is used as multi cast delegate later.
    //Client 002: Declare the delegate. We are going to use it as Multicast delegate
    OrderShipment.OrderProcessingMethods orderprocess;
  3. Five delegate instances are created and they point to one of the five methods implemented by the OrderShipment class.
    //Client 003: Create Delegate Instances
    OrderShipment.OrderProcessingMethods process1 = 
        new OrderShipment.OrderProcessingMethods(deliverorders.GetShoppingCartItems);
    OrderShipment.OrderProcessingMethods process2 = 
        new OrderShipment.OrderProcessingMethods(deliverorders.CalculateOrderPrice);
    OrderShipment.OrderProcessingMethods process3 = 
        new OrderShipment.OrderProcessingMethods(deliverorders.CalculateDiscount);
    OrderShipment.OrderProcessingMethods process4 = 
        new OrderShipment.OrderProcessingMethods(deliverorders.AwordFreeGifts);
    OrderShipment.OrderProcessingMethods process5 = 
        new OrderShipment.OrderProcessingMethods(deliverorders.GetOrderConfirmation);
  4. Before processing the order for normal customer, a delegate chain is formed by adding the delegate created on the previous step. Once the individual delegates are combined using the + operator, the result is stored in the orderprocess delegate. Now, the orderprocess delegate holds the delegate chain and we call it as Multicast delegate. The multicast delegate is ready, it is passed to the OrderShipment class member function ProcessOrderShipment. When a call is made on the multicast delegate, the delegate invokes all the functions currently in the chain. So for the Normal customer, we do not want to provide gift and/or discounts. Hence, those corresponding functions are not part of the delegate chain. Also note that the chained functions are called in the same order they are added to the chain. Code and picture is given below:

    DelegateEvents1/Pic3.JPG

    //Client 004: Process Order for Normal Customer. Order Id: 1000. Customer id 1000.
    Console.WriteLine("=====================================================");
    Console.WriteLine("Process Normal Customer");
    Console.WriteLine("=====================================================");
    orderprocess = process1 + process2 + process5; //Note you can use += operator also
    deliverorders.ProcessOrderShipment(orderprocess,1000,1000);
  5. Next comes the VIP customer. As he is eligible for gift as well as discounts, we need to add the corresponding functions to the multicast delegate orderprocess. Note the current delegate order in the chain. Process5 delegate is Order confirmation, which should be moved last. So process5 delegate removed from the chain, then process3 and process4 delegates are added to the chain. And finally, process5 delegate is put back before making the call to ProcessOrderShipment. Note the usage of += operator. To add a delegate, you can use += operator. And to remove a delegate from the chain, you can use -= operator.
    //Client 005: Process Order for VIP Customer. VIP eligible for Gift and discounts
    //Order Id: 1001. Customer id 1001.
    Console.WriteLine("=====================================================");
    Console.WriteLine("Process VIP Customer");
    Console.WriteLine("=====================================================");
    orderprocess -= process5;    //Remove Order confirmation from chain. 
                //[It should be at location 5]
    orderprocess += process3;     //Add the Process 3 and 4
    orderprocess += process4;
    orderprocess += process5;     //Put back the process 5. 
                //Because order confirmation should be the last step.
    deliverorders.ProcessOrderShipment(orderprocess,1001,1001);
  6. The last customer serviced is a regular customer. I hope no explanation is required now as you are familiar with multicast delegate or delegate chaining.
    //Client 006: Process Order for Regular customer. 
    //Regular customer is not eligible for Gifts, 
    //but enjoy discounts. So revoke the gifting process
    Console.WriteLine("=====================================================");
    Console.WriteLine("Process Regular Customer");
    Console.WriteLine("=====================================================");
    orderprocess -= process4;
    deliverorders.ProcessOrderShipment(orderprocess,1002,1002);

12. Custom Events

An event is kind of ‘Something Happened’. Some examples are the button got pressed; a check mark from the check box is removed. We all know that we call this kind of action as Events happening to those controls. So let us consider a form that has a button in it. And we all know that a button can be clicked. The user does the action of clicking a button and we do not know when that action will happen. In this situation, if we consider the application wants to know when the button got clicked and wants to say “Hi! There”. So what you think now. Somebody should say button is clicked and somebody should respond back to the event in our case saying just a “Hi”. I can hear you are saying “Not a big deal. Double click the button, the Development environment will bring me to a function, and write the code there that says Hi”. Well. If the Team Lead (Yes, the same guy who always bugs you) asks you “Hey! We have a class called Product Stock and it maintains the stock in hand in an integer variable. Can you expose an event say LowStock so that the interested one will get a notification when the stock in hand goes below five?” What is the answer? “Yes”? OK, then you already know the Events very well. It is your choice whether you want to refresh it here or not. If your answer is “Hey. I know how to handle the UI events. I have not created my own event”, then this article is for you. Let us start.

13. Publish and Subscribe

If we go back to the button click on the form that says “Hi there”, there are some pieces of information we need to know.

  1. The button is placed on the form. The form is the container that holds the button, which is an UI Component.
  2. The Button class in .NET exposes an event called Click. So button class publishes the event click.
  3. The Form class wants to know when the button got clicked. So it subscribes for the published event Click.
  4. When the Button is clicked, Subscriber for the event Click on the button is notified. And there is handler code that says Hi when the notification is received. So the publish is nothing but exposing the event and subscribe is kind of getting the notification on the supplied function. Delegate and event are tightly coupled. We will see how when we are going through an example and at the same time you can answer the question for your team lead.

14. The Example

In this example, we have two classes. One is the ProductStock class, which maintains current stock of the product. The other class is Counter used by the billing counter computers in the retail shop. Let us say A customer comes to billing a counter, informs the product he want to purchase, pays the bill and goes to the stored room to receive the product. Each billing counter receives an notification when the product stock goes low. Consider the below picture before we move on:

DelegateEvents1/Pic1.jpg

  1. The ProductStock class publishes the event LowStock
  2. Purchase, Counter, etc. classes subscribe to the Published event LowStock
  3. ProductStock sends a notification to the entire subscriber when the ProductStock goes low. In our example, we are not going to implement Purchase and Someother class.

15. The ProductStock Class

  1. The ProductStock has two member variables. One to know the product name and other one is keep track of the current stock. The current stock gets reduced by the sales counter when a sale of the product is performed.
    //001_1: Member Variable.
    public string ProductName;
    private int StockInHand;
  2. The class declares a multicast delegate OnStockLow that takes an Event source object and EventArgs object. Event source is ProductStock as it is going to raise the notification (Event). EventArgs is usually a class derived from it EventArgs that can be used to pack the information related to the event. To keep this example simple, I have not derived any object from EventArgs. Below is the declaration of Multicast delegate:
    //001_2: Multicast delegate type that get coupled with the event.
    public delegate void OnStockLow(object sender, EventArgs e);
  3. Next, the event StockLow is declared. Note how the Delegate is coupled with the Event. It implies that notification handler function should return void and receive object as first parameter and EventArgs as second parameter. As it is a multicast delegate, you can use delegate chain of the above said functions. OK, now the Product stock published the Event. Below is the declaration of the event:
    //001_3: Published event (StockLow), that takes responsibility of sending 
    //notification to the subscriber through the above 
    //Specified multicast delegate
    public event OnStockLow StockLow;

    Note the syntax: public event

  4. The constructor of the ProductStock class initialized the members ProductName and StockInHand. Below is the code:
    //001_4: Constructor that Initializes the Stock
    public ProductStock(string Name, int OpeningStock)
    {
      ProductName = Name;
      StockInHand = OpeningStock;
    }
  5. The Counter objects calls the ReduceStock function when a sales is performed. This function reduces the current stock and notifies the subscriber of the LowStock event when the current stock goes less than five. Below is the function implementation:
    //001_5: This function reduces the stock based on the sales on the billing counters. 
    //When the stock in hand is lower than 
    //                5, it raises the event StockLow event.
    public void ReduceStock(int SalesDone)
    {
        StockInHand = StockInHand - SalesDone;
        if (StockInHand < 5 )
        {
            EventArgs arg = new EventArgs();
            StockLow(this, arg);
        }
    }

Note that in the above function call to StockLow(this,arg) is known as raising the event or sending a notification. We are done with the ProductStock class.

16. The Counter Class

  1. The counter class declares the member variable for the counter name and the constructor initializes the Name. The Sales function takes the ProductStock and number of product sold. It makes a call to the ReduceStock after the sales. Below is the Implementation code:
    //002: This class for Sales Counter that performs the Sales on 
    //different counters and makes the billing. This class
    //Subscribe to the Published event and 
    //Receives notification through Multicast delegate.
    public class Counter
    {
        //002_1: Class member
        private string CounterName;
    
        //002_2: Constructor for Counter
        public Counter(string Name)
        {
            CounterName = Name;
        }
    
        //002_2: Function that records the sales performed on the counter
        public void Sales(ProductStock prod, int howmuch)
        {
            Console.WriteLine("{0} Sold {1} numbers", prod.ProductName, howmuch);
            prod.ReduceStock(howmuch);
        }
  2. Counter class implements the notification handler for StockLow. Note that the arguments and the void return type. Because, the rule is expected by the delegate OnLowStock coupled with the event. Below is the handler:
    //002_3: Function that acts as event handler 
    //for LowStock to receive the notification
    public void LowStockHandler(object Sender, EventArgs e)
    {
        Console.WriteLine("Announcement on {0}: Stock of Product {1} gone Low",
            CounterName , ((ProductStock) Sender).ProductName );
    }

17. Client Code

Let us see how the client code works. Before that, a small refresh on what we did. The ProductStock class exposes an event StockLow and that event is coupled to OnStockLow delegate. ReduceStock function raises the StockLow event when the product stock goes below five. The counter class implements the notification handler LowStockHandler to receive the notification. Where is the piece of code that links the LowStockHandler to the StockLow event? We will link that in the client code.

  1. First the client creates the two billing counter objects. Below is the code for billing counter:
    //Client 001: Create Billing Counters
    Counter billing_counter1 = new Counter("Sindhu");
    Counter billing_counter2 = new Counter("Ganga");
  2. Next three ProductStock objects are created. These products will be sold through two counters. Below is code:

    //Client 002: Create the Product Stocks
    ProductStock prod1 = new ProductStock("Godrej Fridge", 7 );
    ProductStock prod2 = new ProductStock("Sony CD Player", 6);
    ProductStock prod3 = new ProductStock("Sony DVD", 800);
  3. Next, we subscribe to the event LowStock published by the ProductStock class by creating the Delegate that points to the Notification handler function implemented in the Counter class. Below is the code:

    //Client 003: Couple the Event with the Handler through the Delegate.
    prod1.StockLow += new ProductStock.OnStockLow(billing_counter1.LowStockHandler);
    prod2.StockLow += new ProductStock.OnStockLow(billing_counter1.LowStockHandler);
    prod1.StockLow += new ProductStock.OnStockLow(billing_counter2.LowStockHandler);
    prod2.StockLow += new ProductStock.OnStockLow(billing_counter2.LowStockHandler);
  4. Everything setup. So let as start selling the products and see the notification we stock goes below
  5. I recommend you to put a break point on the below piece of code and examine how the Events work. Below is the price code:

    //Client 004: Now Let us Start serving the customers on the Queue on each counter
    billing_counter1.Sales(prod1, 1);
    billing_counter2.Sales(prod1, 2);
    billing_counter2.Sales(prod3, 70);
    billing_counter2.Sales(prod2, 1);
    billing_counter1.Sales(prod2, 3);
    billing_counter1.Sales(prod3, 5);

Output is shown below:

DelegateEvents1/Pic2.jpg

Closing Notes

  1. The attachment has three examples. When you unzip, it will deliver three solutions in three different zip files.
  2. All the three solutions are written in .NET 2003. If you have the latest version of the IDE, say yes to the Conversion Dialog.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here