Home > Enterprise >  Why delegates inside List are always equal when inserted through for-loop but not equal without loop
Why delegates inside List are always equal when inserted through for-loop but not equal without loop

Time:12-01

Let's look at the following code example. There is a List of delegates and later two instances are inserted inside that list sequentially.

class Client
{
    delegate void ActionListener(string message);
        
    public static void Main()
    {
        List<ActionListener> _actionListenerList = new List<ActionListener>();

        _actionListenerList.Add((message) => Console.WriteLine(message));
        _actionListenerList.Add((message) => Console.WriteLine(message));

        if (_actionListenerList[0].Equals(_actionListenerList[1]))
        {
            Console.WriteLine("Two Delegates are equal");
        }
        else
        {
            Console.WriteLine("Two Delegates did not match");
        }
    }
}

Output:

Two Delegates did not match

Now, if we modify the code and add the delegates inside the list using for loop, then the output is completely different.

class Client
{
    delegate void ActionListener(string message);
        
    public static void Main()
    {
        List<ActionListener> _actionListenerList = new List<ActionListener>();

        for (int i = 0; i < 2; i  )
        {
            _actionListenerList.Add((message) => Console.WriteLine(message));
        }

        if (_actionListenerList[0].Equals(_actionListenerList[1]))
        {
            Console.WriteLine("Two Delegates are equal");
        }
        else
        {
            Console.WriteLine("Two Delegates did not match");
        }
    }
}

Output:

Two Delegates are equal

I guess, my question needs no further explanation - why is the difference produced after using for-loop?

CodePudding user response:

Note that the language specification permits both of the lambdas here to be converted to the same delegate instance:

_actionListenerList.Add((message) => Console.WriteLine(message));
_actionListenerList.Add((message) => Console.WriteLine(message));

Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance.

So you might see two different delegate instances being created on your compiler, but on another compiler, the lambdas could be converted to the same delegate instance, as an optimisation.

To make sure that you absolutely have different delegate instances (though I think this is rarely useful), you can capture a local variable inside the loop (suggested by Jon Skeet). Now the lambdas don't have the "same set of captured local variables":

for (int i = 0; i < 2; i  )
{
    int x = 0;
    _actionListenerList.Add(
        (message) => {
            Console.WriteLine(message);
            _ = x;
        }
    );
}

CodePudding user response:

_actionListenerList.Add((message) => Console.WriteLine(message));
_actionListenerList.Add((message) => Console.WriteLine(message));

In this version, there are two different functions. A lambda is just a one-line function, so if you have two of them then delegates to each of them will not be equal.

Effectively it's as if you wrote:

_actionListenerList.Add(Lambda1);
_actionListenerList.Add(Lambda2);

void Lambda1(string message)
{
    Console.WriteLine(message);
}

void Lambda2(string message)
{
    Console.WriteLine(message);
}

for (int i = 0; i < 2; i  )
{
    _actionListenerList.Add((message) => Console.WriteLine(message));
}

In this version, there is only one function, so every delegate refers to the same function.

It's as if you wrote

for (int i = 0; i < 2; i  )
{
    _actionListenerList.Add(Lambda1);
}

void Lambda1(string message)
{
    Console.WriteLine(message);
}
  • Related