2014-10-17 70 views
4

所以我做了几个接口,像这样:如何使用显式接口事件?

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.

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

+0

http://stackoverflow.com/questions/2268065/c-sharp-language-design-explicit-interface-implementation-事件 – mybirthname 2014-10-17 15:27:48

+0

我明白如何使用显式事件语法,但这不是我的问题;我要求澄清粗体文本,声称存在“重大的内存开销” – 2014-10-17 15:33:45

回答

1

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

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

在这个意义上,事件关键字产生类似于从C#自动实现的属性生成的代码的代码。

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

我们如何明确消除事件的开销?我们在像VG.net这样的库中使用这种方法,并且Microsoft在其代码中使用了类似的方法:将一组事件保存在单个字段中。在大多数情况下,很少有事件拥有许多事件订阅者。最简单的集合是类实例的链表。集合中的每个元素由包含以下属性的类实例组成:

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

当您需要为订户添加事件处理程序时,请使用唯一事件类型标识符查找集合中的委托。您第一次查找时,收藏品不会包含它。在添加事件处理程序的情况下,您将为您的集合添加一个元素,并在该元素中使用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高效。

理想情况下,我们应该有编程工具,不要求我们明确优化数据结构。确切地指定对象在内存中的布局方式是一个由Profiler或智能运行系统更好处理的负担。在这方面,我们仍然处于石器时代,在我曾经工作过的每种编程语言中。