Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何处理显式接口事件?_C#_Events_Interface_Delegates_Explicit Interface - Fatal编程技术网

C# 如何处理显式接口事件?

C# 如何处理显式接口事件?,c#,events,interface,delegates,explicit-interface,C#,Events,Interface,Delegates,Explicit Interface,所以我做了一些如下的界面: public interface IDrawActions : ISimpleDrawable { Action<GameTime> PreDrawAction { get; set; } Action<GameTime> PostDrawAction { get; set; } event EventHandler PreDrawActionChanged; event EventHandler PostDr

所以我做了一些如下的界面:

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

    event EventHandler PreDrawActionChanged;
    event EventHandler PostDrawActionChanged;
}
公共接口IDrawActions:ISimpleDrawable
{
动作预绘制动作{get;set;}
Action PostDrawAction{get;set;}
事件处理程序PreDrawActionChanged;
事件事件处理程序PostDrawActionChanged;
}
任何实现这些接口中的这个(或几个)的类都变得有点混乱,所以我认为使用显式接口实现来隐藏不常见的事件和属性是有意义的。但是,这样做,我得到了一个编译器错误:

事件的显式接口实现必须使用事件访问器语法

通过谷歌搜索,我发现:

这暗示了编写自己的添加和删除访问器的主要原因之一:提供自己的底层数据存储。您可能希望这样做的一个原因是,如果您的类上有很多公开的事件,但在任何时间点,实例上通常只使用少数事件在这种情况下,维护每个事件的委托字段可能会产生大量内存开销。


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

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

  • 包含委托的字段。代理形成链接列表。这是列表的标题,并在标题处插入附加内容
  • 事件访问器,其中add方法使用delegate字段插入到链表中,remove方法从链表中删除。链表的添加和删除也有语法隐藏,因此您只能看到“+=”和“-=”以添加到代理列表或从代理列表中删除
从这个意义上讲,事件关键字生成的代码与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即使在内存中有数百万个图形对象的情况下(即使在低端硬件上运行)也非常有效


理想情况下,我们应该拥有不会强迫我们显式优化数据结构的编程工具。精确指定对象在内存中的布局方式是一个负担,由探查器或智能运行时系统更好地处理。在这方面,我们仍然处于石器时代,在我所使用过的每种编程语言中。

我了解如何使用显式事件语法,但这不是我的问题;我要求澄清粗体文本,它声称存在“显著的内存开销”