#371 – Delegate Basics

In C#, a delegate allows you to create an object that refers to a method.  This object can be thought of as a sort of pointer to the method.

You use the delegate keyword to define a type that can be used to create objects that point to methods having a particular signature.

private delegate void StringHandlerDelegate(string s);

In this example, we define a new delegate type that knows how to point to methods that take a single parameter, of type string, and whose return type is void.

The term delegate refers to this new delegate type.  We can now create an instance of the delegate that points to a specific method.

    static void Main()
    {
        StringHandlerDelegate del1 = Method1;
    }

    static void Method1(string text)
    {
        Console.WriteLine(text);
    }

We can now invoke the method through the delegate instance.

    del1("Invoked thru delegate");
Advertisements

#370 – Subscribe to an Event by Adding an Event Handler

You subscribe to a particular event in C# by defining an event handler–code that will be called whenever the event occurs (is raised). You then attach your event handler to an event on a specific object, using the += operator.

Below is an example where we define an event handler for the Dog.Barked event.  Each time that Kirby barks, we’ll record the date and time of the bark in a list.

        private static List<DateTime> barkLog = new List<DateTime>();

        static void Main()
        {
            Dog kirby = new Dog("Kirby", 12);

            kirby.Barked += new EventHandler(kirby_Barked);

            kirby.Bark();
            Console.ReadLine();

            kirby.Bark();
            Console.ReadLine();
        }

        // Neither argument is used, for the moment
        static void kirby_Barked(object sender, EventArgs e)
        {
            // Log Kirby's barks
            barkLog.Add(DateTime.Now);
        }

Assuming that the Dog class fires its Barked event whenever we call the Bark method, our handler will get called whenever Kirby barks.

#369 – Multiple Clients Can Subscribe to the Same Event

An event in C# is a mechanism that allows an object to inform client code about something that happened inside the object that the other code might care about.

It’s often the case that there are several different pieces of client code that care about the same event.  In other words, when an object raises an event, there may be one subscriber listening for the event, no subscribers, or many subscribers.

Here’s an example that shows two subscribers to the same Dog.Barked event.

#368 – How Events Work

An event in C# is a mechanism that allows an object to inform client code about something that happened inside the object that the other code might care about.

For example, you might have a Dog object that raises a Barked event whenever someone calls the dog’s Bark method.  Your code can be notified whenever the dog barks.

In .NET, we say that the Dog object raises the Barked eventThe logging code handles the Barked event in a method known as an event handler.

Events in C# implement something known as the Publish/Subscribe pattern.  The Dog class publishes information about when the dog barks and the logging code subscribes to that information. 

This is also known as the Observer pattern.  The logging code observes the Dog object and the Dog object notifies any observers whenever the dog barks.

#367 – Iterating through All Possible Values of an Enumeration Type

There are occasions when it’s desirable to iterate through all possible values of an enumeration type.  You can do this using the static Enum.GetValues method.

Suppose that we have an enumeration type representing the days of the week.

    public enum Days { Sun, Mon, Tues, Wed, Thurs, Fri, Sat };

We might want to iterate through all possible values of the Days enumeration, for example to initialize some data structure.

            private Dictionary<Days, LogMessage> dailyMessages = new Dictionary<Days, LogMessage>();

            foreach (Days day in Enum.GetValues(typeof(Days)))
            {
                dailyMessages[day] = new LogMessage("Default message");
            }

#366 – Defining an Indexer with More than One Parameter

An indexer will typically have a single parameter, often based on an integer type.

        public LogMessage this[int i]
        {
            get { return messages[i]; }
            set { messages[i] = value; }
        }

You can also define an indexer that takes more than one parameter.  In the example below, we have an internal data structure that stores a list of messages for each day of the week.  We can retrieve a specific message by passing in a parameter representing the day, as well as a 0-based index into the list of messages for that day.

            // Get 1st Saturday message
            LogMessage msg = log[Days.Sat, 0];

Here’s what the definition of the indexer looks like.

        private Dictionary<Days, List<LogMessage>> dailyMessages = new Dictionary<Days, List<LogMessage>>();

        public LogMessage this[Days day, int i]
        {
            get { return dailyMessages[day][i]; }
        }

#365 – Overloading an Indexer

You can define several versions of an indexer in a class, each indexed using a different type, overloading the indexer.

In the example below, we can index using the Days enumerated type or a 0-based integer representing the day.

    public class Logger
    {
        private Dictionary<Days, LogMessage> dailyMessages = new Dictionary<Days, LogMessage>();

        public LogMessage this[Days day]
        {
            get { return dailyMessages[day]; }
            set { dailyMessages[day] = value; }
        }

        public LogMessage this[int i]
        {
            get { return dailyMessages[(Days)i]; }
            set { dailyMessages[(Days)i] = value; }
        }
    }

Using this class, we could then index using either the Days or the integer type.

            Logger log = new Logger();

            log[Days.Mon] = new LogMessage("Monday was a good day");
            log[2] = new LogMessage("Tuesday not bad either");

In this example, we indexed into the same internal data object.  But we could have also indexed into different objects, depending on the indexer’s type.