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.

8
  • you should subscribe the event like this : yourclassInstance.numberChanged+=()=>{}; or numberChanged+=functionSignature; Commented Jul 16, 2015 at 18:58
  • @HadiHassan Can you clarify on what comes after the +=? Commented Jul 16, 2015 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). Commented Jul 16, 2015 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. Commented Jul 16, 2015 at 19:02
  • 1
    Creating a Minimal, Complete, and Verifiable Example would make it easier for others to help you debug. Commented Jul 16, 2015 at 19:04

3 Answers 3

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.
Sign up to request clarification or add additional context in comments.

6 Comments

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 Nothing technically.
@sab669 It's just a syntactic sugar introduced in C# 2.0
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.
@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?
|
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

2 Comments

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 msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx
thank you for the hint (sometimes in my SO euphoria I tend to forget about the guidelines), I've updated my code.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.