Копирование подписчиков событий в производный объект - PullRequest
0 голосов
/ 10 января 2019

Вот надуманный пример, который у меня есть:

using System;
namespace Example
{
    class Parent
    {
        public event EventHandler Event;
    }
    class Child :Parent
    {
        public string Data {get;set;}

        public Child Copy()
        {
            Child copy = new Child()
            {
                Data = this.Data
            }
            copy.Event = Event.GetInvocationList(); //This doesnt work
            return copy;
        }
    }
}

Поскольку ребенок не может сделать все, что родительский класс может сделать с событием, я не могу использовать .GetInvocationList (). Следовательно, как я мог бы обойти это простым способом, чтобы я мог копировать подписчиков события, сделанного в базовом классе, в дочерний класс?

Самое главное, предположим, что у меня нет доступа к родительскому классу, т. Е. Я наследую класс в System.

1 Ответ

0 голосов
/ 11 января 2019

Когда вы используете Event.GetInvocationList() в классе Parent, вы получаете доступ не к событию, а к автоматически сгенерированному полю делегата, имя которого совпадает с именем события. Это закрытый член, поэтому вы не можете получить к нему доступ из производного класса.

Однако, объявив событие явным добавлением / удалением методов доступа, вы можете решить эту проблему:

class Parent
{
     protected EventHandler Handler { get; private set; }

     public event EventHandler Event
     {
          add => Handler += value;
          remove => Handler -= value;
     }
}

class Child : Parent
{
    private void SomeMethodInDerivedClass()
    {
         var handlers = Handler.GetInvocationList();
         // ...
    }
}

Обновление:

Если вы не можете изменить базовый класс, вы можете получить доступ к полю частного делегата с помощью отражения.

Примечание : Чтобы скопировать поле делегата, вам не нужно получать список вызовов, поскольку вы не можете назначить массив делегатов обработчику событий. Все делегаты C # получены из MulticastDelegate, поэтому назначаемое значение уже будет содержать все подписки. Обратите также внимание, что даже многоадресные делегаты являются неизменяемыми, поэтому не бойтесь назначать исходный экземпляр делегата копии: когда вы добавляете новую подписку к исходному экземпляру, она не будет отражаться в копии (поэтому это работает в некоторой степени аналогично строки).

И решение:

public Child Copy()
{
    Child copy = new Child()
    {
        Data = this.Data
    };

    FieldInfo eventBackingField = typeof(Parent).GetField(nameof(Event), BindingFlags.Instance | BindingFlags.NonPublic);
    if (eventBackingField == null)
        return; // oops, not an auto event, it has explicit accessors

    // copy.Event = this.Event:
    eventBackingField.SetValue(copy, eventBackingField.GetValue(this));
    return copy;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...