如何通过反射清除 class 中的所有静态事件处理程序?

How to clear all static event handlers in a class via reflection?

我有带有大量静态事件回调的 STATIC class。我不想硬编码重置方法,而是想使用反射来自动清除所有事件订阅。


public static void NotifyA(int p1) { notifyA?.Invoke(p1); }
public static event Action<int> notifyA;

public static void NotifyB(float p1) { notifyB?.Invoke(p1); }
public static event Action<float> notifyB;

// and so on...

我卡在这个阶段不知道如何真正清除事件。

    public static void ResetEvents()
    {
        var type = typeof(ClientEvents);

        var events = type.GetEvents(BindingFlags.Static | BindingFlags.Public);

        foreach (var current in events )
        {
            // current.RemoveEventHandler(???);

        }
    }

通常,在 .NET 中“删除所有订阅者”是没有意义的。事件甚至不需要记住他们的订阅者是谁!一个事件实际上只是一对方法,称为 addremove,它们将某种类型的委托作为参数,您可以在这些方法中做任何您想做的事情,例如

public static event Action<int> MySillyEvent {
    add {
        Console.WriteLine($"{value.Method.Name} is being added!");
    }
    remove {
        Console.WriteLine($"{value.Method.Name} is being removed!");
    }
}

现在告诉我,“从 MySillyEvent 中删除所有订阅者”是什么意思?

如果您将范围限制在 field-like events,这是由委托字段支持的事件,那么这是一个更容易处理的问题。您只需将支持事件的委托字段设置为 null。您的 notifyAnotifyB 事件是类似字段的事件。

但是,委托字段的名称是依赖于实现的,EventInfo不知道它是否是类字段。

如果您的 class 没有任何显式声明的静态委托字段,您可以在 class 中找到所有非 public、委托类型的静态字段:

var fields = typeof(ClientEvents).GetFields(BindingFlags.Static | BindingFlags.NonPublic)
            .Where(x => x.FieldType.BaseType.IsSubclassOf(typeof(Delegate)));
foreach (var field in fields) {
    field.SetValue(null, null);
}

否则,您需要依赖一些实现细节,例如 。我们假设字段名称与事件名称相同。这至少适用于我正在使用的编译器。

var type = typeof(ClientEvents);
var fields = type.GetEvents(BindingFlags.Static | BindingFlags.Public)
    .Select(e => type.GetField(e.Name, BindingFlags.Static | BindingFlags.NonPublic));
foreach (var field in fields) {
    field.SetValue(null, null);
}

如果ResetEvents方法在同一个静态class中,那么你甚至不需要反射:

//public static event Action<int> notifyA;
private static Action<int> notifyAHandler;
public static event Action<int> NotifyA
{
    add => notifyAHandler += value;
    remove => notifyAHandler -= value;
}


public static void ResetEvents()
{
    notifyAHandler = null;
    // ...
}

如果你真的想使用反射,那么迭代委托类型的静态字段,并将它们设置为 null。

备注: 按照惯例,所有带有数据的事件都应该使用 EventHandler<TEventArgs> 委托,即使 sendernull 对于静态事件.