MC++ 中的带内存的事件






3.46/5 (3投票s)
2004 年 6 月 2 日
3分钟阅读

32234

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();
}
};