Видели предыдущий пост? Как вам такое продолжение истории:

using System;

struct Foo {
  public void M() { Console.Write("uups!"); }
}

class Bar {
  static event Action E = delegate { };
  static void Main() {
    var foo = new Foo();

    E += foo.M;
    E -= foo.M;

    E(); // ???
  }
}

Отписки снова не происходит! Более того, снаружи такого события отписку такого метода произвести невозможно.

Дело тут в определении типа Foo, являющегося типом-значением, и скрытом боксинге, происходящем при подписке и отписке. Для того, чтобы сделать делегат из метода уровня экземпляра, определённого для типа-значения, надо как-то в делегат сохранить это экземпляр значения (так же, как туда сохраняется this при создании делегатов из методов уровня экземпляра, определённых в классах). Это нельзя сделать никак иначе, кроме как вызвав боксинг значения и “прикрепив” бокс к делегату, так как время жизни делегата часто неопределено и он должен создаваться на куче.

Тогда что происходит при отписке? Всё очень просто - создаётся ещё один бокс значения. И тут становится понятно, что невозможно выяснить, что два бокса были сделаны из одного и того же значения. Реализация Delegate.Equals не должна и не полагается на типы-значения, определяющие свои понятия эквивалентности (переопределяющие Equals/реализующие IEquatable<T>), а руководствоваться понятием ссылочной эквивалентности, которого не прослеживается между двумя разными боксами одного значения. Именно поэтому, не смотря на равенство методов, подписанный на событие делегат не равен делегату, с помощью которого происходит попытка отписки. Делегатов, равных сходному просто не может больше существовать!