msiyer
msiyer

Reputation: 841

Regarding Events and Delegates in C#

What are the side-effects of considering Events as instances of Delegates Type?

Jon Skeet says, "Events aren't delegate instances.", here.. I would not have asked this if I had read this anywhere else.

I had always, for the past 2 months, visualized Events as special type of Delegate with the event keyword just helping in preventing the nullification of the Delegate being invoked via the Event.

Could someone please elaborate on how to properly visualize the whole concept for someone new to C# and event based programming?

EDIT 1:

I understood the concepts of delegates and consequently events turned out to be a very simple concept. I would like to go ahead and add an example conjured by me during my battle with these constructs. I have added a lot of comments for better understanding. This is meant for new guys like me:

THE LIBRARY DLL:

namespace DoSomethingLibrary
{
    /*
     *This is a public delegate declared at the base namespace level for global presence.
     *The question is WHY do we need to have a DELEGATE here?
     *The answer is: I do not want to implement the LOGGING logic. Why? Well, my consumers are many
     *and all are equally demanding. They all need different types of logging. Some need HTML logging, 
     *some need XML logging for their custom log analyzer, some need plain text logging etc...
     *This is hell for me. How am I going to support all their demands. I cannot. Thus, I ask them to 
     *implement LOGGING on their side. I am providing an INTERFACE(literal sense) in the guise of a DELEGATE.
     *A DELEGATE is a HOOK.
     *This is the hook that is needed for consumers to hook their custom loggers into the library.
     */
    public delegate void Logger(string firstParam, string secondParam);

    public class PrintingManiac
    {
        public Logger printingManiacConsumerLoggerHook;
        public void StartPrintingLikeAManiac()
        {
            for (int iterator = 0; iterator <= 3; iterator++)
            {
                /*This loop is an emulator which I am using to emulate some huge processing or some huge job.
                 *Let us imagine that this is a library that does some heavy data crunching OR some 
                 *extremely complex data access job etc..
                 */
                Console.WriteLine("Actual WORK - " + iterator.ToString());
                /*After each step this library tries to LOG. But NOTE that this library
                 *has no LOGGER implemented. Instead, this library has judiciously DELEGATED
                 *the logging responsibilty to the CONSUMER of this library.
                 */
                printingManiacConsumerLoggerHook("Actual Work", "Step " + iterator.ToString());
            }
        }
    }
}

THE CONSUMER EXECUTABLE:

/*
 * Let us assume that I have purchased the DoSomethingLibrary DLL from a vendor.
 * I have to add the DLL as a reference to my executable's project in Visual Studio.
 * I also have to use the DoSomethingLibrary namespace to access the Logic in the DLL.
 */
using DoSomethingLibrary;

namespace UnderstandingDelegates
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
             * Creating an object of the lone class PrintingManiac in the DoSomethingLibrary
             */
            PrintingManiac newManiac = new PrintingManiac();

            /*
             * HOOKING my custom logger to the DoSomethingLibrary DLL.
             * I get the best of both the worlds. I have a well-tested and efficient library working for me
             * AND I have the best logging avaliable.
             * The DoSomethingLibrary DLL has no knowledge of what logging this executable is going to use.
             * This executable has to just satisfy the requirements of the DELEGATE signature of DoSomethingLibrary DLL.
             */
            newManiac.printingManiacConsumerLoggerHook += new Logger(ClientsCustomizedLoggerTwo);

            newManiac.StartPrintingLikeAManiac();
            Console.ReadLine();
        }

        public static void ClientsCustomizedLoggerOne(string firstParam, string secondParam)
        {
            /*
             *This logger has '=' used as a decorator
             *In real scenarios the logger may be very complex.
             *Let us assume this is an HTML logger
             */
            Console.WriteLine("=============================");
            Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
            Console.WriteLine("=============================");
        }

        public static void ClientsCustomizedLoggerTwo(string firstParam, string secondParam)
        {
            /*
             *This logger has '-' used as a decorator
             *Let us assume this is an XML logger
             */
            Console.WriteLine("------------------------------");
            Console.WriteLine("Delegated Logging IN CONSUMER code " + firstParam + " - " + secondParam);
            Console.WriteLine("------------------------------");
        }
    }
}

EDIT 2:

I have written an article in CodeProject to clearly explain the whole concept of delegates.

Upvotes: 0

Views: 641

Answers (3)

supercat
supercat

Reputation: 81149

I think one of the biggest sources of confusion regarding events in C# stems from the fact that an event declaration which does not specify explicit add and remove methods creates a delegate with the same name as the event (a design I dislike, btw; VB.NET by default gives the delegate a different name). Thus, declaring an event foo actually declares both an event named foo and a delegate named foo; the name foo will sometimes refer to the delegate and sometimes return to the event.

An event in .NET is really nothing more than a pair of methods, each of which accepts a delegate. One of the methods is called "add", and should cause the method to cause the supplied delegate to be invoked if/when/whenever some particular condition arises. The other method is called "remove", and should ask that a "subscription" associated with a particular delegate be cancelled. Note that nothing about the general event contract requires that an event do anything with a passed-in delegate. An immutable collection could implement an "observable collection" interface but simply ignore any requests to add or remove change notifications [the observable-collection contract would require that all delegates added to the "collection change" event be called when the collection changes, but since there would be no circumstance where the collection could actually change, there would be no circumstance where it would need to call the passed-in delegates].

By default, when a class one declares an event eventName in C#, the compiler also declares a variable eventName of the delegate type associate with the event. Any statements of the form eventName += someDelegate will get translated as eventName_add(someDelegate) and statements of the form eventName -= someDelegate will get translated as eventName_remove(someDelegate). All other references to eventName will be regarded as references to the delegate of that name. Note that in older versions of C#, if the delegate was in scope, statements of the += and -= forms would operate directly on the delegate rather than on the event. This meant that while subscription requests received from outside a class would go through add/remove methods (which would use locking or interlocked methods to provide thread-safety), those processed within a class would not be thread-safe. Later versions of C# always regard eventName += someDelegate and eventName -= someDelegate as add/remove requests, however, even when the delegate is in scope.

Upvotes: 1

Hans Passant
Hans Passant

Reputation: 941417

how to properly visualize the whole concept

Nobody ever has much trouble visualizing properties in C#. A property is an accessor to a field, it prevents other code from directly manipulating the field value. Such code is forced to call the get and the set accessors to access the field. You can put arbitrary code in the accessor methods, throwing an exception when you are not happy with the value passed to the setter for example, very common. The underlying storage for the property doesn't have to be a field either, you can expose the field or property of another class object for example. Etcetera.

A good way to visualize an event is to compare it to a property. With the exact same intent, it prevents other code from directly manipulating a delegate object. They have to go through the add and remove accessors. The calls to these methods are generated by syntax sugar in the client code, the += operator calls add(), -= calls remove(). Comparable to the syntax sugar for accessing properties, you don't explicitly write the call to the get or set method either.

What is confusing about events and makes them seem so different from properties is that the event accessor methods are optional. If you don't write them then the C# compiler will auto-generate them for you. Including the backing store, a delegate object. Properties can also have auto-generated accessors and backing store, automatic properties do. But the syntax is different, you still have to declare them.

Using the auto-generated accessors is very common. That code is almost always good enough, it already provides the guarantee that arbitrary code cannot remove the event subscriptions of other code. Not that many good reasons to write your own. One is cutting down on the size of your class object if you support a lot of events. Instead of having a delegate object for each individual event, you can store them in an EventHandlerList instead. Very common in .NET framework code for example. The extra indirection is also taken advantage of in WPF's attached events and WinRT's eventing model with isn't based on delegates.

Upvotes: 3

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61952

An event consists of two special methods, called accessors, namely add and remove. Both take in one value parameter value of the same delegate type, and return void.

For example this is an event:

public event Action Exploded
{
  add
  {
    Console.WriteLine("Hello from 'add'. Type of 'value' is '{0}'.", value);
  }
  remove
  {
    Console.WriteLine("Hello from 'remove'. Type of 'value' is '{0}'.", value);
  }
}

A "field-like" event is a compiler-generated type of event where there is a private generated backing field of the delegate type in question, and where the add accessor adds value to the invocation list of that backing field (delegate combination) whereas the remove accessor removes value from that list. This is done in a smart thread-safe way.

Upvotes: 2

Related Questions