在运行时使用反射检查是否订阅了给定事件

发布于 2024-10-22 02:11:19 字数 1612 浏览 0 评论 0原文

考虑一个有一些事件的类。该事件列表将会不断增长。有些是可选的。其他的都是需要的。

为了简化一些初始验证,我有一个自定义属性,将事件标记为必需事件。 例如:

    [RequiredEventSubscription("This event is required!")]
    public event EventHandler ServiceStarted;

到目前为止一切顺利。 为了验证所有事件,我使用反射迭代事件列表并获取自定义属性。 但我需要一种方法来确定该事件是否已订阅。

无需反射,ServiceStarted.GetInitationList 即可完成这项工作。但该事件必须来自此列表: var eventList = this.GetType().GetEvents().ToList();

有没有办法检查事件列表中的给定事件是否使用反射订阅?

- [更新] - 这是基于 Ami 的回答的一个可能的解决方案:

    private void CheckIfRequiredEventsAreSubscribed()
    {
        var eventList = GetType().GetEvents().ToList().Where(e => Attribute.IsDefined(e, typeof(RequiredEventSubscription)));

        StringBuilder exceptionMessage = new StringBuilder();
        StringBuilder warnMessage = new StringBuilder();

        foreach (var evt in eventList)
        {
            RequiredEventSubscription reqAttr = (RequiredEventSubscription) evt.GetCustomAttributes(typeof(RequiredEventSubscription), true).First();
            var evtDelegate = this.GetType().GetField(evt.Name, BindingFlags.Instance | BindingFlags.NonPublic);
            if (evtDelegate.GetValue(this) == null)
            {
                warnMessage.AppendLine(reqAttr.warnMess);
                if (reqAttr.throwException) exceptionMessage.AppendLine(reqAttr.warnMess);
            }
        }
        if (warnMessage.Length > 0)
            Console.WriteLine(warnMessage);
        if (exceptionMessage.Length > 0)
            throw new RequiredEventSubscriptionException(exceptionMessage.ToString());
    }

非常感谢!

Consider a class that has some events. This event list is going to grow. Some are optional. Others are required.

To simplify some initial validation I have a custom attribute that marks an event as a required one.
For example:

    [RequiredEventSubscription("This event is required!")]
    public event EventHandler ServiceStarted;

So far so good.
To validate all events, using reflection I iterate the event list and grab the custom attributes.
But I need a way to determine if the event is subscribed or not.

Without reflection the ServiceStarted.GetInvocationList does the job. But the event must come from this list:
var eventList = this.GetType().GetEvents().ToList();

Is there any way to check if a given event, from an event list, is subscribed using reflection?

--[Update]--
Here's a possible solution based on Ami's answer:

    private void CheckIfRequiredEventsAreSubscribed()
    {
        var eventList = GetType().GetEvents().ToList().Where(e => Attribute.IsDefined(e, typeof(RequiredEventSubscription)));

        StringBuilder exceptionMessage = new StringBuilder();
        StringBuilder warnMessage = new StringBuilder();

        foreach (var evt in eventList)
        {
            RequiredEventSubscription reqAttr = (RequiredEventSubscription) evt.GetCustomAttributes(typeof(RequiredEventSubscription), true).First();
            var evtDelegate = this.GetType().GetField(evt.Name, BindingFlags.Instance | BindingFlags.NonPublic);
            if (evtDelegate.GetValue(this) == null)
            {
                warnMessage.AppendLine(reqAttr.warnMess);
                if (reqAttr.throwException) exceptionMessage.AppendLine(reqAttr.warnMess);
            }
        }
        if (warnMessage.Length > 0)
            Console.WriteLine(warnMessage);
        if (exceptionMessage.Length > 0)
            throw new RequiredEventSubscriptionException(exceptionMessage.ToString());
    }

Many thanks!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

纸伞微斜 2024-10-29 02:11:19

这里存在一些主要的设计问题。一般来说,无法询问对象其事件的订阅者是谁。任何人都想要这个功能是非常不寻常的,但如果你真的想要它,你应该让类以某种方式公开它,例如,通过使用如下方法实现接口:

public IEnumerable<Delegate> GetSubscribers(string eventName);

无论如何,要回答所提出的问题,你< em>可以使用反射,但前提是您确切地知道订阅者是如何维护的。例如,
假设所有事件都是使用当前的 C# 类字段事件实现来实现的,您可以执行类似的操作(强烈不鼓励):

object o = ...

var unsubscribedEvents = 
  from e in o.GetType().GetEvents()
  where Attribute.IsDefined(e, typeof(RequiredEventSubscriptionAttribute))
  let field = o.GetType()
               .GetField(e.Name, BindingFlags.NonPublic | BindingFlags.Instance)
               .GetValue(o)
  where field == null
  select field;

var isValid = !unsubscribedEvents.Any();

There are some major design issues here. In general, there's no way to ask an object who its events' subscribers are. It's highly unusual that anyone would want this functionality, but if you really want it, you should get classes to somehow expose this, for example, by implementing an interface with a method such as:

public IEnumerable<Delegate> GetSubscribers(string eventName);

Anyway, to answer the question as asked, you can use reflection, but only if you know exactly how the subscribers are being maintained. For example,
assuming that all events are implemented with the current implementation of C# field-like events, you can do something like (strongly discouraged):

object o = ...

var unsubscribedEvents = 
  from e in o.GetType().GetEvents()
  where Attribute.IsDefined(e, typeof(RequiredEventSubscriptionAttribute))
  let field = o.GetType()
               .GetField(e.Name, BindingFlags.NonPublic | BindingFlags.Instance)
               .GetValue(o)
  where field == null
  select field;

var isValid = !unsubscribedEvents.Any();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文