0

The code example tells the story of the question. Here it is as a Fiddle.

I expected the Actions to behave like "normal" reference type instances such as List<Action>.

using System;

public class Program
{
    static Action action1;
    static Action action2;
    public static void Main()
    {
        // the Both method goes to both action1 and action2
        // that is what I expected
        action1 = Both;
        action2 = action1;

        // anything now assigned to action1 only goes to action1
        action1 += OnlyAction1;

        // and anything now assigned to action2 only goes to action2
        action2 += OnlyAction2;

        foreach (var d in action1.GetInvocationList())
            Console.WriteLine(d.Method.Name);

        foreach (var d in action2.GetInvocationList())
            Console.WriteLine(d.Method.Name);

        // since both actions have the same HashCode, 
        // I expected both actions to have the same invocation list, 
        Console.WriteLine(action1.GetHashCode());
        Console.WriteLine(action2.GetHashCode());
    }

    public static void Both() {}
    public static void OnlyAction1() {}
    public static void OnlyAction2() { }
}

Output:

Both
OnlyAction1
Both
OnlyAction2
828401262
828401262
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467

1 Answers1

2

Equal hash code does not say objects are equal.

If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two objects do not have to return different values.

Different hash codes mean objects are different, but not vice versa.

Read more GetHashCode method: https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx

From another answer:

Delegates are immutable, so the reference obtained in that code is guaranteed to not change. If a user subscribes or unsubscribes after the null check, a new delegate will be created and set to the event.

Backs
  • 24,430
  • 5
  • 58
  • 85
  • That's helpful. Now, why do these two actions have different invocation lists? – Shaun Luttin Jun 08 '18 at 03:24
  • @ShaunLuttin here is answer https://stackoverflow.com/questions/1609430/copying-delegates – Backs Jun 08 '18 at 03:33
  • @ShaunLuttin: Why would you expect them to have the same invocation list when you're deliberately giving them *different* invocation lists? `action1` has an invocation list of `Both` and `OnlyAction1`; `action2` has an invocation list of `Both` and `OnlyAction2`. Is that not what you intended? I think it's more surprising that their hash codes *are* the same, but as Backs said, that's acceptable. – Jon Skeet Jun 08 '18 at 04:58
  • @DaisyShipton I expected them to have the same invocation list, because I mistakenly thought that delegates act like normal reference types. Does that make sense to you? – Shaun Luttin Jun 08 '18 at 19:24
  • @ShaunLuttin: Not really - it's like having `string x = ""; string y = ""; x += "Both"; y += "Both"; x += "OnlyX"; y += "OnlyY";`. Would you expect `x` and `y` to have the same values? Assuming you wouldn't, where do you see the difference between this and your code using delegates? – Jon Skeet Jun 08 '18 at 21:05
  • @DaisyShipton Strings are odd in the same way that delegates are odd. That is, strings are immutable reference types. Most reference types are not immutable. Take the example of `List` instead, and you might understand my initial confusion. – Shaun Luttin Jun 08 '18 at 23:39
  • @ShaunLuttin: But on a `List` you call `Add` which returns void and mutates the list. You don't use `list += item;` - although I could imagine an *immutable* list doing that. It's important to note that you're using `+=`, which is assigning back to the variable again. It's not that strings and delegates have magical behavior here (well, not this aspect). They just happen to be immutable, and you can design your own classes to behave the exact same way - "mutable" and "immutable" are not a matter of "normal" or "odd". Anyway, it sounds like you understand now. – Jon Skeet Jun 09 '18 at 05:41
  • @DaisyShipton That was helpful to understand the `+=` operator further. E.g. https://dotnetfiddle.net/g2SmhC makes more sense now. Also, my calling strings and delegates "odd" was the wrong choice of words. What I meant is that they are "less common." Since reference types are mutable by default, we need to work harder to make them immutable; as a result, immutable reference types are less common than mutable ones are, it's in that sense that they are odd/unusual/uncommon. So, their immutability comes as a surprise when I haven't first done my research on the type. – Shaun Luttin Jun 09 '18 at 15:44
  • @DaisyShipton Reference types are rare in the set of built-in immutable types: https://stackoverflow.com/a/31722118/1108891 – Shaun Luttin Jun 09 '18 at 15:53
  • @ShaunLuttin: Relatively rare, sure - although I'd hope less so in app-specific code. (There are the immutable collections these days of course.) Anyway, I think we're probably at the end of this being productive - I'm glad it all makes sense now. – Jon Skeet Jun 09 '18 at 16:23