На каком экземпляре класса делегат вызывается? - PullRequest
5 голосов
/ 25 марта 2011

Рассмотрим этот код,

public class A
{
     //...
     void f()
     {  
          B b = new B();
          b.SomeEvent += this.SomeMethod;
     }
     void SomeMethod() {}
 }

 public class B
 {
     //...

     public event SomeEventHandler SomeEvent;

     void h()
     {  
          if ( SomeEvent != null )
          {
               SomeEvent.invoke();
          }
     }
 }

В этом фрагменте кода SomeEvent.invoke() фактически вызывает SomeMethod() класса A.Итак, на данный момент у меня есть несколько вопросов:

  • На каком экземпляре A вызывается SomeMethod?Как B узнает экземпляр, на котором будет вызван делегат?Как здесь работает CLR?
  • Кроме того, SomeMethod является приватным методом, тогда как B может вызывать этот метод извне класса A?

РЕДАКТИРОВАТЬ:

Прочитав несколько первых ответов, я узнал, что Delegate имеет свойство Target, для которого вызывается делегат.Но я не мог понять, на каком именно этапе устанавливается это свойство Target?Кто это установил?Когда я пишу b.SomeEvent += this.SomeMethod;, оно также устанавливает свойство Target?Как именно?

Ответы [ 4 ]

3 голосов
/ 25 марта 2011
 b.SomeEvent += this.SomeMethod

Здесь много сахара, который мешает вам увидеть, что на самом деле происходит.Записано, это похоже на это:

 b.SomeEvent.add(new MulticastDelegate(this, SomeMethod));     // not legal code

Где add () является аксессором добавления для события, компилятор автоматически генерирует его, когда вы явно не объявите свой собственный.Первый аргумент конструктора делегата - это экземпляр объекта, о котором вы спрашиваете, свойство Target объекта делегата.Обратите внимание, что это имеет побочные эффекты, подписка на событие сохраняет ссылку на ваш объект b.Это предотвращает сбор мусора, что было бы довольно плохо при вызове события.

Это также может быть проблемой, вы можете непреднамеренно утечь ссылку на объект.В вашем коде нет хорошего способа отменить подписку на обработчик событий, поэтому объект A будет жить так же долго, как и объект B, для которого вы вызвали h ().

2 голосов
/ 25 марта 2011

Делегат содержит целевую ссылку для вызова метода. Вы можете проверить это с помощью свойства Delegate.Target. В этом случае он будет вызван для экземпляра, для которого вызывается f. (Это ноль, если вы вызываете статический метод.)

Что касается конфиденциальности - это только одна из особенностей делегатов. Вы можете создать делегата в коде, который имеет доступ к закрытому методу, но вы можете запустить его где угодно Думайте об этом как о реализации интерфейса, просто вызывая закрытый метод из реализации открытого интерфейса.

2 голосов
/ 25 марта 2011

На каком экземпляре A вызывается SomeMethod? Откуда B знает экземпляр, в котором должен быть вызван делегат? Как здесь работает CLR?

Делегат фактически содержит ссылку на фактический экземпляр. Это заставит его вызывать конкретный экземпляр, на котором он был вызван.

Кроме того, SomeMethod является закрытым методом, тогда почему B может вызывать этот метод извне класса A?

Он не выполняет метод напрямую - он выполняет делегат . Здесь есть разница - закрытое ограничение применяется только к методу, но поскольку сам класс создает делегат, у него есть доступ к методу, поэтому все работает нормально.

1 голос
/ 25 марта 2011
  • Какой бы экземпляр не использовался при присоединении к событию
  • Потому что A передал представление делегата за пределы класса.

Конкретный экземпляр Delegate имеет свойство Target; это представляет экземпляр, который будет использоваться при вызове делегата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...