Class 如何将WithEvents关键字与全局变量一起使用?

Class 如何将WithEvents关键字与全局变量一起使用?,class,events,vb6,module,Class,Events,Vb6,Module,我试图在VB6模块中声明一个变量,如下所示: Public WithEvents MyObject As MyClass 帮助文件指出,WithEvents只能在类模块中使用。为什么不能在.bas模块中使用 我正在处理的遗留代码在模块中全局声明了一个对象。我想将with events添加到此声明中,但我需要保持对象的全局性,因为许多其他表单等都引用该对象。如何在对代码中断最小的情况下实现这一点?VB中的事件是一个相当复杂的COM机制的包装器。本质上,在这种机制中,涉及的所有内容都必须是实现接口

我试图在VB6模块中声明一个变量,如下所示:

Public WithEvents MyObject As MyClass
帮助文件指出,
WithEvents
只能在类模块中使用。为什么不能在
.bas
模块中使用


我正在处理的遗留代码在模块中全局声明了一个对象。我想将
with events
添加到此声明中,但我需要保持对象的全局性,因为许多其他表单等都引用该对象。如何在对代码中断最小的情况下实现这一点?

VB中的事件是一个相当复杂的COM机制的包装器。本质上,在这种机制中,涉及的所有内容都必须是实现接口IConnectionPoint或IConnectionPointContainer的COM类。BAS模块是遗留BASIC的一部分,与VB类不同,它不是作为COM类实现的。我想他们本可以在COM中将BAS文件重新实现为单例类型的对象(就像他们对全局多用途类所做的那样),但他们没有。因此,不幸的是BAS文件不能与事件一起使用

至于问题的最简单解决方案,您需要利用对象变量只是对对象的引用,而不是对象本身的引用这一事实。我想您希望事件消息到达所需的最高级别对象,以便您能够控制您感兴趣的应用程序的位。确认这个地方。例如,您可能有一个主窗体,您确信它将是第一个加载的对象,最后一个卸载的对象。作为此对象初始化的一部分,传入对全局对象的引用,并将其设置为WithEvents模块级别变量。你现在控制了局面


但是!!这是非常重要的!!您必须确保在应用程序关闭之前及时清除此引用,以防您的表单(或其他任何形式)有任何循环引用。在这种情况下,Form_Unload事件非常理想。

我不确定您的应用程序有多大,但有几种方法可以做到这一点。下面是我要做的,尽管这可能需要你15分钟左右的时间来重构你的应用程序来使用它

首先将模块中的所有公共和全局方法及成员复制到一个名为(例如)clsApplication的类中

其次,在现在为空的模块(我们称之为modGlobal)中,将
公共属性Get Application()声明为clsApplication
。继续添加一个二传手

第三,在启动程序的
Sub Main()
中,将其添加为第一行
Set modGlobal.Application=New clsApplication

现在,您已经将模块替换为一个全局类,该类可以侦听在应用程序范围内发生的事件。在应用程序的其余部分中,您可以访问全局状态,如
Application.Config
Application.GetUser()
或您在全局级别保留的任何其他内容

当然,您可以将其应用于希望与事件一起使用的变量,但无论如何,您都应该真正考虑从模块中获取代码


刚刚看到@Mark的评论。微小的方法是我一直坚持的。如果事件类是MyEventSource,则创建一个名为MyEventSourceListener的类,该类具有一个名为
Target
的属性,您将对象传递到该属性中,并使用Events私下声明该属性。然后MyEventSourceListener可以接收事件并将它们转发回您的模块


我不喜欢它,因为它是一种黑客行为,将代码放回模块中,但这可能是最方便的方法。

编写一个类,接受全局对象作为参数并接收其事件

' Class MySink
Private WithEvents m_oSink As MyClass

Friend Sub frInit(oSink As MyClass)
    Set m_oSink = oSink
End Sub

Private Sub m_oSink_MyEvent()
    '--- implement event
End Sub
.bas
模块中创建此类的实例

Public g_oMyObject AS MyClass
Private m_oMySink As MySink

Sub Main()
    Set g_oMyObject = New MyClass
    Set m_oMySink = New MySink
    m_oMySink.frInit g_oMyObject
End Sub

一种简单方法的案例

对于一个类,甚至是一个用户控件(实际上只是一种特殊的类),在整个程序中具有广泛使用的属性和方法,有时是非常有用的

如果主要关注的是广泛使用无状态的“实用工具”方法(例如,一些与Web相关的方法具有URL编码、实体编码等方法),那么只需声明一个附加的全局实例“没有”事件,就可以相当便宜,而不必导致加载大量内部状态(数组、集合等)。由于您的程序无论如何都需要所有的代码,并且内存中只有一个副本,所以第二个实例可能相当便宜

另一种选择是将这些“公共”操作甚至属性值分解成单独的类或静态(BAS)模块

但有时,您有一个相当复杂的用户控件或类,您不想通过重构为单独的模块或以其他方式修改来自定义它。也许您可以维护一个通用的标准版本,以便在不同的项目中使用。也许这是一个第三方图书馆,你甚至没有它的来源。不管怎样

需要考虑的事情

这给我们带来了另一种可能对你有用的技术,即使用欧文·梅恩韦不朽的话来说,这是“不适合盲童的”。如果你有足够的经验来使用它,那么你应该已经想到了——但我们中没有人有完美的记忆力(如果我们确实花时间阅读过那本精美的手册)

因此,也许出于某种原因,这对你不起作用,你已经考虑并放弃了这个想法

您的容器对象(表单、UserControl、父类等)可以在初始化时小心地设置对相关对象实例的全局引用,并在终止时删除引用。这样做时应注意避免循环引用或孤立引用,因为循环引用或孤立引用会阻止其他对象甚至整个程序卸载

约翰尼后藤不应该轻易地接受或使用这一点。但是如果你知道你在VB中做什么,并且已经阅读并理解了t