微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 如何使用显式接口事件?

所以我做了一些像这样的接口:

public interface IDrawActions : ISimpleDrawable
{
    Action<GameTime> PreDrawAction { get; set; }
    Action<GameTime> PostDrawAction { get; set; }

    event EventHandler PreDrawActionChanged;
    event EventHandler PostDrawActionChanged;
}

实现这些(或几个)这些接口的任何类都变得混乱,所以我认为使用显式接口实现隐藏不常见的事件和属性是有意义的.但是,这样做,我得到一个编译器错误

An explicit interface implementation of an event must use event accessor Syntax

谷歌搜索引导我到this rather helpful blog post

This hints at one of the primary reasons for writing your own add and remove accessors: to provide your own underlying data store. One reason you might want to do this is if you have lots of exposed events on your class,but in such a way that only a few are typically in use on an instance at any point in time. In such a scenario,there can be significant memory overhead associated with maintaining a delegate field for each event.

这究竟如何节省资源?似乎事件的委托调用列表将为null,但是如果您使用自己的自定义处理程序,它实际上将如何以及何时实例化?一切都隐藏了!

解决方法

粗体文本是指内存优化,当您有许多事件或许多对象实例时,它们都非常有用.在C#中创建事件的最基本支持是使用event关键字.此关键字是以下生成代码的语法糖:

>包含代理的字段.代表形成链表.这是列表的头部,并在头部插入添加项.
>事件访问器,其中add方法使用委托字段插入链接列表,remove方法链接列表中删除.链接列表的添加删除也有隐藏它的语法糖,因此您只能看到“=”和“ – =”添加到委托列表或从委托列表中删除.

从这个意义上说,event关键字产生的代码类似于C#自动实现属性生成代码.

开销是为每个事件维护一个单独的字段.这不是必需的,因为没有必要为支持类暴露的每个属性的数据维护单独的字段.我们可以虚拟化事件字段和属性字段.

我们如何具体消除事件的开销?我们在诸如VG.net之类的库中使用此方法,并且Microsoft在其代码中使用类似的方法:在单个字段中保留事件的集合.在大多数情况下,很少有实例有很多事件订阅者.最简单的集合是类实例链接列表.集合中的每个元素都包含一个包含以下属性类实例

>事件标识符.每种独特类型的事件都有一个唯一标识符.最好使用小的东西,比如字节或整数,因为你不太可能有数百万种事件类型,即使是在一个巨大的库中也是如此.
>代表.代表可以是弱类型(委托).

当您需要为订阅添加事件处理程序时,可以使用唯一的事件类型标识符在集合中查找委托.第一次查找时,该集合将不包含它.在添加事件处理程序的情况下,您将向集合中添加一个元素,并在该元素中,使用Delegate.Combine添加到存储在那里的委托.要删除处理程序,请使用Delegate.Remove.

以下是VG.net中实际代码的示例:

private static readonly int MouseDownEvent = EventsProperty.CreateEventKey();

    public event ElementMouseEventHandler MouseDown
    {
        add { AddHandler(MouseDownEvent,value); }
        remove { RemoveHandler(MouseDownEvent,value); }
    }

    public virtual void OnMouseDown(ElementMouseEventArgs args)
    {
        ElementMouseEventHandler handler = 
            FindHandler(MouseDownEvent) as ElementMouseEventHandler;
        if (handler != null)
            handler(this,args);
    }

    internal void AddHandler(int key,Delegate value)
    {
        EventsProperty p = (EventsProperty)GetorInsertProperty(EventsProperty.Key);
        p.AddHandler(key,value);
    }

    internal void RemoveHandler(int key,Delegate value)
    {
        EventsProperty p = (EventsProperty)GetProperty(EventsProperty.Key);
        if (p == null)
            return;
        p.RemoveHandler(key,value);
    }

    internal Delegate FindHandler(int key)
    {
        EventsProperty p = (EventsProperty)GetProperty(EventsProperty.Key);
        if (p == null)
            return null;
        return p[key];
    }

我们不仅虚拟化了事件,还虚拟了属性.对于VG.net,所有事件都包含在一个虚拟属性(EventProperty)中,并且大多数公共属性也是虚拟化的,尽管我们将最可能一起使用的属性值捆绑在一起.这使我们能够在所有实例上提供许多属性和事件,而每个实例的这些属性或事件使用的内存为零,除非:

>对于属性,该属性设置为非认值.
>对于事件,某事订阅了该事件.

即使在低端硬件上运行,即使内存中有数百万个图形对象,这些类型的优化也使VG.net高效.

理想情况下,我们应该拥有不会强制我们明确优化数据结构的编程工具.准确指定对象在内存中的布局方式是分析器或智能运行时系统更好地处理的负担.在这方面我们仍处于石器时代,在我曾经使用的每种编程语言中.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐