Search code examples
c#.netdelegatesnull-conditional-operator

Does the null-conditional operator function the same with delegates and regular objects?


Reference

I'm currently dealing with some thread sensitive code.

In my code I have a list of objects that is manipulated by two different threads. One thread can add objects to this list, while the other may set it to null.

In the above reference it specifically mentions that for delegates:

myDelegate?.Invoke()

is equivalent to:

var handler = myDelegate;
if (handler != null)
{
    handler(…);
}

My question is, is this behavior the same for say, a List<>? Eg:

Is:

var myList = new List<object>();    
myList?.Add(new object());

guaranteed to be equivalent to:

var myList = new List<object>();

var tempList = myList;
if (tempList != null)
{
    tempList.Add(new object());
}

?


EDIT:

Note that there is a difference between (how a delegate works):

var myList = new List<int>();
var tempList = myList;
if (tempList != null)
{
    myList = null; // another thread sets myList to null here
    tempList.Add(1); // doesn't crash
}

And

var myList = new List<int>();
if (myList != null)
{
    myList = null; // another thread sets myList to null here
    myList.Add(1); // crashes
}

Solution

  • In this answer, Eric Lippert confirms that a temporary variable is used in all cases, which will prevent the "?." operator from causing a NullReferenceException or accessing two different objects. There are, however, many other factors which can make this code not thread safe, see Eric's answer.

    UPD: to address the claim that a temporary variable is not created: there is no need to introduce a temp variable for a local variable. However, if you try to access something that may conceivably be modified, a variable gets created. Using the same SharpLab with slightly modified code we get:

    using System;
    using System.Collections.Generic;
    
    public class C {
        public List<Object> mList;
    
        public void M() {
            this.mList?.Add(new object());
        }
    }
    

    becomes

    public class C
    {
        public List<object> mList;
    
        public void M()
        {
            List<object> list = mList;
            if (list != null)
            {
                list.Add(new object());
            }
        }
    }