Why would an event be null? (object reference not set to an instance of an object)

11,620

Solution 1

You have a race condition:

usbforProcessExited gets subscribed in the constructor of b and might be invoked before you called rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus).

You should only call usbfor.Start() after you subscribed to SetStatusEvent.

A related problem is that the event will run on another thread. You should set rsSet.SynchronizingObject before starting the process so your event handler can modify the form without manually calling Invoke/BeginInvoke.

Solution 2

The event is null, if there are no subscribers.

There are two solutions:

  1. Initialize the event when declaring (dummy subscriber doing nothing):

    public event SetStatus SetStatusEvent = delegate { };
    
  2. Check the event for null before raising (in a thread-safe way):

    public void usbforProcessExited(object sender, EventArgs f)
    {
        SetStatus setStatus = SetStatusEvent;
        if (setStatus != null)
        {
            setStatus(100, "DONE", Color.Green);
        }
    }
    

Solution 3

Jon Skeet has taught me that in c# 6.0 you can also use:

SetStatusEvent?.Invoke(100, "DONE", Color.Green);;

Solution 4

The event is null, if noone subscribed to it yet. So it's a good practice to control on null equality, like:

    public void usbforProcessExited(object sender, EventArgs f)
    {
        if(SetStatusEvent!=null)
            SetStatusEvent(100, "DONE", Color.Green);
    }

That's why outside it works well, as you have, this line:

 rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus);

so subscription and initilizaton of the event.

When you call it from inside, there is no any subscription made, so event is null.

EDIT

Following the comments let's provide more thread safe approach of handling null reference check on event:

   public void usbforProcessExited(object sender, EventArgs f)
    {
        var ev = SetStatusEvent; //[1]
        if(ev!=null) //[2]
            ev(100, "DONE", Color.Green);
    }

Remember that assignment operation ia atomic in CLR, so even in between lines [1] and [2] someone else reset event to null, your ev will be still valid and code will execute without crashing. IF this is desired behaviour or not it depends on your concrete case, so this is just another option to manage null reference control on event in thread-safe way.

Share:
11,620
daygoor
Author by

daygoor

Updated on June 08, 2022

Comments

  • daygoor
    daygoor almost 2 years

    I have a form with a button, a label and a progress bar, so that when I click the button it creates an instance of class b to run a process. Once the process is done it will call an EventHandler to show "done" in the main form's label!

    I created an event (SetStatusEvent) of a delegate (SetStatus) to do this. And it seems fine when I call this event outside the EventHandler (usbforProcessExited) but when I call it from usbforProcessExited it gives an error -

    object reference not set to an instance of an object
    

    main form

    public partial class main : Form
    {
        b rsSet = new b();
    
        public main()
        {
            InitializeComponent();
            rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus);
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            rsSet.FormatUSB();
        }
    
        private delegate void UpdateStatus(int i, string str, Color clr);
    
        private void SetStatus(int i, string str, Color clr)
        {
            this.progressBar1.Value = i;
            this.lbl_status.ForeColor = clr;
            this.lbl_status.Text = str;
        }
    
        private void updateStatus(int i, String msg, Color color)
        {
            object[] p = GetInokerPara(i, msg, color);
            BeginInvoke(new UpdateStatus(SetStatus), p);
        }
    
        private object[] GetInokerPara(int progress, string msg, Color color)
        {
            object[] para = new object[3];
            para[0] = progress;
            para[1] = msg;
            para[2] = color;
    
            return para;
        }
    }
    

    class b

    class b
    {
        public delegate void SetStatus(int i, string msg, Color color);
        public event SetStatus SetStatusEvent;
    
        System.Diagnostics.Process usbfor = new System.Diagnostics.Process();
    
        public void FormatUSB()
        {
    
            usbfor.StartInfo.FileName = @"usbformat.bat";
            usbfor.EnableRaisingEvents = true;
            usbfor.Exited += new EventHandler(usbforProcessExited);
            usbfor.Start();
        }
    
        public void usbforProcessExited(object sender, EventArgs f)
        {
            SetStatusEvent(100, "DONE", Color.Green); //ERROR HERE! (object reference not set to an instance of an object
        }
    }
    

    Where is the problem?