Home > Enterprise >  Copying delegates behavior
Copying delegates behavior

Time:11-16

C# 11:

file class C
{
    public int IntField1;
}

file struct S
{
    public int IntField1;
}

file class StructureVsClassWhenCopying
{
    private static void v1()
    {
        System.Console.WriteLine("v1");
    }

    private static void v2()
    {
        System.Console.WriteLine("v2");
    }

    public static void Main()
    {
        C c1 = new();
        c1.IntField1 = 1;
        C c2 = c1;
        c1.IntField1 = 2;
        System.Console.WriteLine(c2.IntField1); // 2, because class is a reference-type

        S s1 = new();
        s1.IntField1 = 1;
        S s2 = s1;
        s1.IntField1 = 2;
        System.Console.WriteLine(s2.IntField1); // 1, because struct is a value type

        string str1 = "old string";
        string str2 = str1;
        str1 = "new string";
        System.Console.WriteLine(str2); // old string, because string is immutable

        System.Action a1 = v1;
        System.Action a2 = a1;
        a1 -= v1;
        a1  = v2;
        a2.Invoke(); //v1. Why?
    }
}

I want to know about the copying of reference and value types. I have understood this example with classes (reference type), struct (value types) and strings (also reference types, but immutable). But delegates also are reference types, why do thay behave like structs and strings?

CodePudding user response:

Assigning a1 to a2 means you are making a copy of the reference contained in a1, which is a reference to v1. At no point does a2 contain a reference to a1. So changing a1 has no effect.

System.Action a1 = v1;  //a1 points to the v1 method
System.Action a2 = a1;  //a2 now points to the v1 method too
a1 -= v1;               //a1 points nowhere
a1  = v2;               //a1 now points at the v2 method
a2.Invoke();            //a2 still points at the v1 method

CodePudding user response:

Because delegates have their own logic. They are multicast delegates. You can think of them as an array of delegates (in fact they are a wrapper around such an array). I.e., you can add several delegates. Every time you add or remove a delegate you get a new instance of this array in return. This ensures a correct behavior in multi-threading scenarios.

e.g.

System.Action a1 = v1; // Equivalent to: System.Action a1 = new Action(v1);
a1  = v2;
a1(); ==> Prints "v1" and "v2";

The C# language reference says in the chapter 19.5 Delegate instantiation:

An instance of a delegate is created by a delegate_creation_expression (§11.7.15.6), a conversion to a delegate type, delegate combination or delegate removal.

which is equivalent in saying that they are immutable.

CodePudding user response:

Sometimes it helps to see what the actual code looks like.

In this case = and -= on Delegates is basically Syntax Candy.

This is the actual code Thank you SharpLab

public static void Main()
{
    C c = new C();
    c.IntField1 = 1;
    C c2 = c;
    c.IntField1 = 2;
    Console.WriteLine(c2.IntField1);
    S s = default(S);
    s.IntField1 = 1;
    S s2 = s;
    s.IntField1 = 2;
    Console.WriteLine(s2.IntField1);
    string text = "old string";
    string value = text;
    text = "new string";
    Console.WriteLine(value);
    Action action = <>O.<0>__v1 ?? (<>O.<0>__v1 = new Action(v1));
    Action action2 = action;
    action = (Action)Delegate.Remove(action, <>O.<0>__v1 ?? (<>O.<0>__v1 = new Action(v1)));
    action = (Action)Delegate.Combine(action, <>O.<1>__v2 ?? (<>O.<1>__v2 = new Action(v2)));
    action2();
}

So

a =v;
a-=v;

Is

a = (Action) Delegate.Combine(a, new Action(v));
a = (Action) Delegate.Remove(a, new Action(v));

Delegate.Combine and Delegate.Remove both create new Delegates, and not mutate the original.

  • Related