4

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.

Community
  • 1
  • 1
JShell
  • 624
  • 2
  • 7
  • 22
  • you should subscribe the event like this : yourclassInstance.numberChanged+=()=>{}; or numberChanged+=functionSignature; – Monah Jul 16 '15 at 18:58
  • @HadiHassan Can you clarify on what comes after the +=? – JShell Jul 16 '15 at 18:59
  • The expression after the "+=" needs to be a delegate of the same type as the event declaration. This could be the name of a declared method, or an anonymous method (which uses the `()=>{}` syntax). – BJ Myers Jul 16 '15 at 19:01
  • it should be like this: yourClass.EventName += FunctionThatHandlesTheEvent(int example, string example2) – kingfrito_5005 Jul 16 '15 at 19:01
  • 2
    Can you post the exact code that is causing the error? Your [something to do here] code is likely causing the issue. – BJ Myers Jul 16 '15 at 19:02
  • @BJMyers Its just a method name that I'd rather not show – JShell Jul 16 '15 at 19:03
  • 1
    Creating a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve) would make it easier for others to help you debug. – BJ Myers Jul 16 '15 at 19:04
  • @JesseShellabarger check my answer, if you need more help go ahead, better in my opinion to define your own delegate to fire the numberchanged with 2 params ( old value and new value) by this it will be more resuable – Monah Jul 16 '15 at 19:13

3 Answers3

6

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.
Community
  • 1
  • 1
Ron Beyer
  • 11,003
  • 1
  • 19
  • 37
  • 1
    While on the topic of programatically adding event handlers, what's the difference from something like this: `myButton.Click += new EventHandler(myButton_Click);` versus `myButton.Click += myButton_Click` ? – sab669 Jul 16 '15 at 19:16
  • 1
    @sab669 Nothing technically. – Ron Beyer Jul 16 '15 at 19:18
  • 2
    @sab669 It's just a syntactic sugar introduced in C# 2.0 – Setsu Jul 16 '15 at 19:26
  • The `IDisposable.Dispose` method is used to release unmanaged resources and is not related to freeing managed memory. The relation between `IDisposable.Dispose` and the garbage collector is that if you have a `Dispose` method that releases unmanaged resources you need to create a finalizer to ensure that the unmanaged resources are released even if `Dispose` is never called. This has nothing to do with leaking event handlers. – Martin Liversage Jul 16 '15 at 19:38
  • @MartinLiversage Sorry, not sure I 100% understood that. At my previous job I got in the habbit of having an `AttachEvents()` method which would attach events with the `new EventHandler()` syntax, then we'd have a `DettachEvents()` method which we'd call from the form designer's `Dispose()` method. Never did learn *why* we did that. I was under the impression that was the GC's job basically. Is that, more or less, what you're referring to? – sab669 Jul 16 '15 at 19:42
  • @MartinLiversage I understand that, however you can use the `Dispose` method to do other things on deconstruction aside from releasing unmanaged resources. For example, placing the object in a `using` block causes the `Dispose` method to be called, where you can perform clean up of the object. The point I was trying to make is that the GC can keep an object alive if you don't get rid of event handlers. – Ron Beyer Jul 16 '15 at 19:44
4

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
keenthinker
  • 7,645
  • 2
  • 35
  • 45
  • 1
    As far as best-practices go, any class that extends the `EventArgs` class should include the words "EventArgs", for example, `NumbersInfo` should be called `NumbersInfoEventArgs`. See https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx – Ron Beyer Jul 16 '15 at 19:56
  • 2
    thank you for the hint (sometimes in my SO euphoria I tend to forget about the guidelines), I've updated my code. – keenthinker Jul 16 '15 at 19:59
1

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

Monah
  • 6,714
  • 6
  • 22
  • 52