65.9K
CodeProject 正在变化。 阅读更多。
Home

MC++ 中的带内存的事件

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.46/5 (3投票s)

2004 年 6 月 2 日

3分钟阅读

viewsIcon

32234

downloadIcon

892

一篇关于使用托管 C++ 向事件添加内存的文章。

引言

委托是 .NET 的一个不错的功能,它使编码回调变得简单。本文不会描述如何创建委托和事件 - CodeProject 上有许多其他文章提供了这些信息。委托不能做的一件事是记住何时调用事件,以便可以将数据发送到添加到事件的任何新委托。

背景

我在工作中遇到了这个问题,因为事件是在系统的所有部分创建和附加之前触发的。这导致一些组件没有正确的数据。需要以最通用的方式解决这个问题。

创建事件访问器

第一步是创建一些委托和一个带有事件的类。对于事件,正常的声明被事件的访问器所取代。

__event void add_E(MyDel* p)
{
    Console::WriteLine("Add E");
    pE = static_cast<MyDel*> (Delegate::Combine(pE,p));
}

__event void remove_E(MyDel* p)
{
    Console::WriteLine("Remove E");
    pE = static_cast<MYDEL*> (Delegate::Remove(pE, p));
}

__event void raise_E()
{
    Console::WriteLine("Raise E");
    if (pE != 0)
        pE->Invoke();
}

这将创建一个名为E的事件。然后创建一个类来接收事件。

public __gc class EventReceiver
{
public:
    void H1() 
    {
        Console::WriteLine("EventReceiver - H1");
    }

    int H2(int i, float f)
    {
        Console::WriteLine("EventReceiver - H2 with args {0} and {1}", 
                                          i.ToString(), f.ToString());
        return 0;
    }
};

然后在该示例的主函数中,创建了两个类的实例,然后将委托附加到事件E。到目前为止,这就像任何其他委托/事件项目一样,如果你要手动创建访问器。当然,编译器通常会自动为你创建访问器。

EventSource* pE = new EventSource();
EventReceiver* pR = new EventReceiver();
EventReceiver2* pR2 = new EventReceiver2();

// hook event handlers for pR
Console::WriteLine("EventReceiver - hooking events");
pE->E += new MyDel(pR, &EventReceiver::H1);
pE->E2 += new MyDel2(pR, &EventReceiver::H2);

// raise the E event
pE->E();

下一步是为事件提供一些内存,以记住何时引发它。如果委托有变量,那么也必须保存这些变量。对于本例中的事件E,只需要一个bool来表示它已被调用。因此,需要对代码进行两处更改 - 首先是对raise_访问器的更改,然后是对add_访问器的更改。

__event void add_E(MyDel* p)
{
    Console::WriteLine("Add E");
    pE = static_cast<MyDel*> (Delegate::Combine(pE,p));
    if(m_eInvoked == true)
        p->Invoke();
}

__event void raise_E()
{
    Console::WriteLine("Raise E");
    if (pE != 0)
    {
        m_eInvoked = true;
        pE->Invoke();
    }
}

使用这种方法,程序员可以控制哪些事件获得这种内存增强,哪些不获得。对于典型的“即发即弃”类型的事件,可能不需要这样做。对于其他事情,“即发即弃”类型的事件可能会重置其他事件的其他变量。一个例子是纸张插入打印机,一个事件可能是发送打印机中可用的纸张,另一个事件可能是纸张已被移除的信号。纸张移除事件可能会清除存储的可用纸张变量。

示例代码将此示例扩展到两个接收实例和两个事件。

C# 的不同之处

我还研究了在 C# 以及 MC++ 中使用事件访问器。我设置了用于添加和删除委托的类和访问器函数,然后添加了一个用于引发事件的访问器。编译器不允许我添加一个用于引发事件的函数。我发现这很奇怪且令人困惑,因为它表明 MC++ 比 C# 具有更多的功能。在进一步研究之后,我发现你必须使用自定义函数来引发事件,而不是访问器。完成此操作后,C# 代码看起来类似于 MC++ 代码。代码包含在本文中,但不包含在下载中,除非人们希望稍后包含它。

public class EventSource 
{
    // private variables for E event
    private    MyDel pE;
    private bool m_eInvoked;

    public EventSource()
    {
        m_eInvoked=false;
    }

    public event MyDel E
    {
        add
        {
            Console.WriteLine("Add E");
            pE += value;
            if(m_eInvoked==true)
                value(); 
        }
        remove
        {
            Console.WriteLine("Remove E");
            pE -= value;
        }
    }
    public void RaiseE()
    {
        m_eInvoked = true;
        if(pE != null)
            pE();
    }
};
MC++ 中的带有内存的事件 - CodeProject - 代码之家
© . All rights reserved.