Can delegates change parameters in delegate chain? (C#)

We have 1 event, 2 subscribers. When event rises 1st subsriber (function) change parameter and this parameter is of reference type. Will the second subscriber get changed parameter or the origin one?

Jon Skeet
people
quotationmark

You can't change the parameter to have a different value assuming it's a by-value parameter, but (as always) you may be able to modify the object that the value refers to.

Example:

using System;
using System.Text;

class Test
{
    static void Main(string[] args)
    {
        Action<StringBuilder> action1 = sb =>
        {
            Console.WriteLine(sb);
            sb = new StringBuilder("x");
        };
        Action<StringBuilder> action2 = sb =>
        {
            Console.WriteLine(sb);
            sb.Append("y");
        };
        Action<StringBuilder> action3 = sb =>
        {
            Console.WriteLine(sb);
            sb.Append("z");
        };

        Action<StringBuilder> all = action1 + action2 + action3;
        StringBuilder builder = new StringBuilder("a");
        all(builder);
        Console.WriteLine(builder);
    }
}

Output:

a
a
ay
ayz

In other words:

  • The first action changes the value of its parameter; this doesn't affect anything else
  • The second action appends "y" to the StringBuilder object its parameter refers to
  • The third action can "see" the change made by the second action, and it appends "z"
  • After the delegates have been invoked, their changes are visible to the caller

This is all exactly the same as regular method calls passing the parameter by value.

Now you can declare a delegate to use ref parameters - at which point it behaves like a method with a ref parameter, and changes to the parameter itself are visible to later delegates in the chain. Here's a version of the above example using a new RefAction<T> delegate:

using System;
using System.Text;

delegate void RefAction<T>(ref T arg);

class Test
{
    static void Main(string[] args)
    {
        RefAction<StringBuilder> action1 = (ref StringBuilder sb) =>
        {
            Console.WriteLine(sb);
            sb = new StringBuilder("x");
        };
        RefAction<StringBuilder> action2 = (ref StringBuilder sb) =>
        {
            Console.WriteLine(sb);
            sb.Append("y");
        };
        RefAction<StringBuilder> action3 = (ref StringBuilder sb) =>
        {
            Console.WriteLine(sb);
            sb.Append("z");
        };

        RefAction<StringBuilder> all = action1 + action2 + action3;
        StringBuilder builder = new StringBuilder("a");
        all(ref builder);
        Console.WriteLine(builder);
    }
}

Now the output is:

a
x
xy
xyz

... which shows that the change to the value of sb in action1 is visible everywhere else.

people

See more on this question at Stackoverflow