2014-10-17 73 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或智能運行系統更好處理的負擔。在這方面,我們仍然處於石器時代,在我曾經工作過的每種編程語言中。