Event Driven Programming

I've been reading this MSDN article and this question to try to understand events in .NET. Unfortunately, its not clicking for me and I'm having a lot of trouble. I'm trying to integrate this technique into my project, with little success.

Basically, I've got this class that will read numbers. Whenever it encounters a new number, I want it to fire an event called numberChanged.

So, I set up my event public event EventHandler numberChanged; . Later on, I fire my event when it encounters a number than isn't the same as the previous one.

if(currentNumber != previousNumber){
     if(numberChanged != null){
          numberChanged(this, new EventArgs());
     }
}

But then I'm having trouble 'subscibing' to this event. If I do numberChanged += [something to do here] it errors saying that numberChanged is an event and not a type.

Is my explanation clear enough for some advice to be offered? Many thanks.


There are a number of ways to handle it, the most basic is to create a function:

public void MyNumberChangedHandler(object sender, EventArgs e)
{
    //Your code goes here that gets called when the number changes
}

You then subscribe (one time only, usually in the constructor) by going:

numberChanged += MyNumberChangedHandler;

Or, you can use something called an anonymous (lambda) method, which is also assigned in your constructor (typically):

numberChanged += (sender, e) => {
    //Your code here to handle the number changed event
};

To expand a little bit, care must be taken when using the lambda approach since you can create memory leaks and zombie objects. The .NET memory garbage collector is a mark-and-sweep system that removes objects when they are no longer in use. This post shows how hard it is to remove lambda event handlers: How to remove a lambda event handler .

Having an active event handler can keep your object alive even if it has been disposed! Here is an example of creating a zombie object (doesn't run in Fiddle but you can copy to your own console app) https://dotnetfiddle.net/EfNpZ5

Prints out:

I'm still alive
I'm still alive
I was disposed!
Press any key to quit
I'm still alive
I'm still alive
I'm still alive.

As everything else in the C# programming world, the events concept also follows specific rules and has it's own syntax. The wording is as follows:

  • an event defined as EventHandler is actually just a shortcut for a special method (delegate) signature - public delegate void EventHandler(object sender, EventArgs e) [1]. Whenever you have a signature in C# you always know what you need to write on the right sight or as a parameter, in order to connect/call some objects/methods/and so on.
  • after the event is defined, you need to subscribe in order to be informed whenever something happens. The syntax for subscribing an event is += . Naturally for unsubscribing is -= . MSDN says that the syntax should be object.event += eventHandler (or object.event += new EventHandler(eventHandler); )
  • so after an event is defined ( event Event SomeEvent; ) all that left is to create a method that can be bound to this event. This method has to have the same signature as the EventHandler, so it should match the signature of [1] and can be something like private void numberChangedEventHandler(object sender, EventArgs eventArguments)
  • Now you know what you need to write on the right side of += .

    An example:

    public class NumberSequence
    {
        // numbers to be compared
        private readonly List<int> numbers = new List<int>();
        // used to generate a random collection
        private readonly Random random = new Random();
        // tell me if the previous and next number are different
        public event EventHandler DifferentNumbersEvent;
    
        public NumberSequence()
        {
            // fill the list with random numbers
            Enumerable.Range(1, 100).ToList().ForEach(number =>
            {
                numbers.Add(random.Next(1, 100));
            });
        }
    
        public List<int> Numbers { get { return numbers; } }
    
        public void TraverseList()
        {
            for (var i = 1; i < this.numbers.Count; i++)
            {
                if (this.numbers[i - 1] != this.numbers[i])
                {
                    if (this.DifferentNumbersEvent != null)
                    {
                        // whoever listens - inform him
                        this.DifferentNumbersEvent(this, EventArgs.Empty);
                    }
                }
            }
        }
    }
    

    Now before the class is used, define the event handler, that will listen and will be called, when the event is fired (wording again):

    private void differentNumberEventHandler(Object sender, EventArgs eventArguments)
    {
        Console.WriteLine("Different numbers...");
    }
    

    And the usage:

    var ns = new NumberSequence();
    ns.DifferentNumbersEvent += differentNumberEventHandler;
    ns.TraverseList();
    

    Everything else is just syntactic sugar for this notation (lambda / anonymous methods / ...), for example:

    object.Event += (s, e) => { // code ... }; is the same as object.Event += (Object sender, EventArgs eventArguments) => { // code ... }; . Do you recognise the signature? - it is the same as the private void differentNumberEventHandler... .

    Often we need to pass information through the event, in this case maybe we want to see the two numbers. C# allows you to do this easily using custom event arguments. Just create a class that inherits the EventArgs class and add properties for the data that should be passed, in this case the numbers:

    public class NumbersInfoEventArgs : EventArgs
    {
        public int Number1 { get; set; }
        public int Number2 { get; set; }
    }
    

    And then specify, when declaring the event, that it will pass data of type NumbersInfoEventArgs (signatures again):

    public event EventHandler<NumbersInfoEventArgs> DifferentNumbersEvent;
    ...
    this.DifferentNumbersEvent(this, new NumbersInfoEventArgs
    {
        Number1 = this.numbers[i - 1],
        Number2 = this.numbers[i]
    });
    

    And last but now least, the signature of the event handler should match the signature of the event:

    private void differentNumberEventHandler(Object sender, NumbersInfoEventArgs eventArguments)
    {
        Console.WriteLine("Different numbers {0} - {1}", eventArguments.Number1, eventArguments.Number2);
    }
    

    And voila, the output is:

    Different numbers 89 - 86
    Different numbers 86 - 53
    Different numbers 53 - 12
    Different numbers 12 - 69
    

    you can subscribe the event in this way:

    using System;
    
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hello World");
            var num = new Number();
            num.numberChanged +=(s,e) =>{
                Console.WriteLine("Value was changed to {0}",num.Value); // in the demo below you can find another implementation for this sample using custom events
            };
            num.Value=10;
            num.Value=100;
        }
    }
    
    public class Number{
        public event EventHandler numberChanged;
        private int _value=0;
        public int Value
        {
            get{
                return _value;
            }
            set{
                if(value!=_value){
                    _value=value;
                    if(numberChanged!=null)
                        numberChanged(this,null);
                }
            }
        }
    }
    

    explanation:

    since the EventHandler delegate has 2 parameters (sender, eventArgs) as mentioned here, you need to pass these params and I passed them as s and e

    another way to subscribe this event like this:

    var num = new Number();
    num.numberChanged += NumberChanged_Event; // below is the delegate method
    
    public void NumberChanged_Event(object sender,EventArgs e)
    {
       // your code goes here
    }
    

    I updated the demo to work with you own delegate to pass the old value and new value which can help in many cases.

    here a working demo

    链接地址: http://www.djcxy.com/p/51444.html

    上一篇: 取消匿名委托事件

    下一篇: 事件驱动的编程