Краткий ответ: это невозможно.Вы не можете хранить события в коллекциях и позже очищать их списки обработчиков.
События служат только одной цели - инкапсуляции.Единственное, что они делают, это предоставляют методы доступа (а именно add
и remove
), поэтому код вне вашего класса может только добавлять / удалять обработчики только в поле делегата поддержки.Эти фрагменты кода в основном одинаковы:
class MyClass
{
public event EventHandler MyEvent;
}
class MyClass
{
private EventHandler myDelegate;
public event EventHandler MyEvent
{
add => myDelegate += value;
remove => myDelegate -= value;
}
}
Но предположим, что мы не используем события, а напрямую используем делегаты.Вы можете создать словарь, в котором ключи являются делегатами, а не событиями, но это не поможет вашей проблеме.Это потому, что делегаты неизменны .Вы не можете сохранить делегата в коллекции, а затем извлечь его и очистить его список вызовов.
Единственное решение здесь будет заключаться в прямой ссылке на каждое событие, как в этом коде.Я не уверен, подойдет ли вам это решение.
using System;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApp3
{
class Program
{
static void Main()
{
var ticker = new Ticker();
var form = new Form();
form.Show();
form.FormClosing += (s, e) => ticker.ClearSubscriptions();
ticker.Ticked += new EventHandler((s, e) => form.Invoke(
new Action(() => form.Text = DateTime.Now.ToString())));
Application.Run();
}
class Ticker
{
public event EventHandler Ticked;
public Ticker()
{
new Thread(new ThreadStart(() => {
while (true)
{
Ticked?.Invoke(this, EventArgs.Empty);
Thread.Sleep(1000);
}
})).Start();
}
public void ClearSubscriptions()
{
Ticked = null;
}
}
}
}
Как видите, ClearSubscriptions
очищает событие Ticked
вручную.Если у вас есть больше событий, вы также должны очистить их вручную и только в классе Ticker
, потому что это единственное место, которое имеет доступ к базовому делегату.Вы можете очистить только те события, которые объявили сами.
Кроме того, вы можете сохранить отдельный список для каждого события.
static void Main()
{
var ticker = new Ticker();
var form = new Form();
form.Show();
var tickedSubscriptions = new List<EventHandler>();
form.FormClosing += (s, e) =>
{
foreach (var subscription in tickedSubscriptions)
{
ticker.Ticked -= subscription;
}
tickedSubscriptions.Clear();
};
var handler = new EventHandler((s, e) => form.Invoke(
new Action(() => form.Text = DateTime.Now.ToString())));
tickedSubscriptions.Add(handler);
ticker.Ticked += handler;
Application.Run();
}
Но, на мой взгляд, это решение далеко не идеальнопотому что вам нужно отслеживать множество отдельных списков.
ОБНОВЛЕНИЕ:
Я подумал о другом решении, которое подходит для вашего случая.Хотя я не уверен, элегантно ли это.
Несмотря на то, что делегаты являются неизменяемыми, ничто не мешает нам создавать объекты-оболочки, которые могут изменить вспомогательного делегата, и поместить эти оболочки в словарь.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApp3
{
class Program
{
private static Dictionary<EventHandlerWrapper, List<EventHandler>> subscriptions =
new Dictionary<EventHandlerWrapper, List<EventHandler>>();
static void Main()
{
var ticker = new Ticker();
var form = new Form();
form.Show();
form.FormClosing += (s, e) =>
{
foreach (var subscription in subscriptions)
{
foreach (var handler in subscription.Value)
{
subscription.Key.Remove(handler);
}
}
subscriptions.Clear();
};
var updateTitle = new EventHandler((s, e) =>
form.Invoke(new Action(() => form.Text = DateTime.Now.ToString())));
ticker.Ticked += updateTitle;
subscriptions.Add(ticker.TickedWrapper, new List<EventHandler> { updateTitle });
Application.Run();
}
class Ticker
{
public event EventHandler Ticked;
public EventHandlerWrapper TickedWrapper;
public Ticker()
{
TickedWrapper = new EventHandlerWrapper(
() => Ticked,
handler => Ticked += handler,
handler => Ticked -= handler);
new Thread(new ThreadStart(() => {
while (true)
{
Ticked?.Invoke(this, EventArgs.Empty);
Thread.Sleep(1000);
}
})).Start();
}
}
class EventHandlerWrapper
{
public Func<EventHandler> Get { get; }
public Action<EventHandler> Add { get; }
public Action<EventHandler> Remove { get; }
public EventHandlerWrapper(
Func<EventHandler> get,
Action<EventHandler> add,
Action<EventHandler> remove)
{
this.Get = get;
this.Add = add;
this.Remove = remove;
}
}
}
}