簡體   English   中英

如何使用顯式接口事件?

[英]How do I work with explicit interface events?

所以我做了一些像這樣的接口:

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

    event EventHandler PreDrawActionChanged;
    event EventHandler PostDrawActionChanged;
}

實現這些(或幾個)這些接口的任何類都變得混亂,所以我認為使用顯式接口實現隱藏不常見的事件和屬性是有意義的。 但是,這樣做,我得到一個編譯器錯誤:

事件的顯式接口實現必須使用事件訪問器語法

谷歌搜索引導我到這個相當有用的博客文章

這暗示了編寫自己的添加和刪除訪問器的主要原因之一:提供您自己的底層數據存儲。 您可能想要這樣做的一個原因是,如果您的類上有大量暴露事件,但這樣的方式通常在任何時間點只有少數幾個在實例上使用。 在這種情況下,可能存在與維護每個事件的委托字段相關聯的顯着內存開銷。

這究竟如何節省資源? 似乎事件的委托調用列表將為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高效。

理想情況下,我們應該擁有不會強制我們明確優化數據結構的編程工具。 准確指定對象在內存中的布局方式是分析器或智能運行時系統更好地處理的負擔。 在這方面我們仍處於石器時代,在我曾經使用的每種編程語言中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM