VB.Net 如何将处理程序附加到泛型 class

VB.Net How to attach a handler to a generic class

我有一个 class 'oBnd' 定义为可以指定为类型 clsBound 或 clsClaim 的对象。 Claim 和 bound 从外部看是相同的,相同的方法等。

我使用 'CallByName' 调用各种属性 即Dim Current As String = CallByName(oBnd, PropName, CallType.Get)

当 class 中的 属性 发生变化时,class 引发 DirtyStatus 事件。

我在附加到此事件时遇到问题。

如果我尝试

 AddHandler oBnd.DirtyStatus, AddressOf oBnd_DirtyStatus

我收到错误 "DirtyStatus is not an event of Object" 我想这是有道理的,因为显然对象对我的脏状态一无所知。

我尝试使用:

 AddHandler DirectCast(oBnd, clsBound).DirtyStatus, AddressOf oBnd_DirtyStatus

虽然这确实修复了错误,但在引发 DirtyStatus 事件时不会调用它。

oBnd 定义为 Private WithEvents oBnd As Object 它是全局的形式

oBnd 设置为

        oBnd = New clsBound(mvarBUDConnection)
        AddHandler oBnd.DirtyStatus, AddressOf oBnd_DirtyStatus

        oBnd.Load(CInt(txtTrans.Text))
        BuildPage(oBnd)

        oBnd = New clsClaim(mvarBUDConnection)
        AddHandler oBnd.DirtyStatus, AddressOf oBnd_DirtyStatus

        oBnd.Load(CInt(txtTrans.Text))
        BuildPage(oBnd)

我要附加的 oBnd_DirtyStatus 子程序看起来像这样

Private Sub oBnd_DirtyStatus(IsDirty As Boolean) ' Handles oBnd.DirtyStatus
    Me.Text = "QFix"
    If IsDirty Then
        Me.Text = "QFix - Pending Save"
        btnSave.Enabled = True
    Else
        btnSave.Enabled = False
    End If
End Sub

如何将句柄附加到此事件?

这里是您既可以使事件正常工作又可以避免使用反射来访问属性的方法。即使给定 the public methods are similar but the data being carried is very different,仍然可以使用 OOP/Inheritance。

Public Enum ClaimBoundType
    None            ' error!!!!
    Claim
    Bound
End Enum

Public MustInherit Class ClaimBase
    ' type tracker usually rather handy
    Public Property ItemType As ClaimBoundType

    Public Sub New(t As ClaimBoundType)
        ItemType = t
    End Sub

    ' low rent INotifyPropertyChanged
    Public Event DataChanged(sender As Object, e As EventArgs)

    ' "universal" prop: works the same for all derived types
    Private _name As String = ""
    Public Property Name As String
        Get
            Return _name
        End Get
        Set(value As String)
            If value <> _name Then
                _name = value
                BaseDataChanged(Me)
            End If
        End Set
    End Property

    ' props which must be implemented; 1 or 100 doesnt matter
    MustOverride Property CurrentValue As Integer

    ' methods which must be implemented
    MustOverride Function DoSomething() As Integer

    ' raise the changed event for base or derived classes
    Protected Friend Sub BaseDataChanged(sender As Object)
        RaiseEvent DataChanged(sender, New EventArgs())
    End Sub
End Class

你必须做一些基本的数据分析来弄清楚哪些属性和方法可以在基础 class 中实现(与上面的 Name 一样),哪些在继承 classes。通常至少有一些可以在基础 class 中完成。

您派生的 classes 可以完全 不同的方式实现方法并从任何地方加载数据:

Public Class Claim
    Inherits ClaimBase   ' the IDE will add all the MustInherits when
                         ' you press enter
    Public Sub New()
        MyBase.New(ClaimBoundType.Claim)
    End Sub

    Public Overrides Function DoSomething() As Integer
        ' what happens here can be completely different 
        ' class to class 
    End Function

    Private _CurValue As Integer = 0
    Public Overrides Property CurrentValue As Integer
        Get
            Return _CurValue
        End Get
        Set(Value As Integer)
            If _CurValue <> Value Then
                _CurValue = Value
                OnDataChanged("CurrentValue")
            End If
        End Set
    End Property

    ' name of prop that changed not actually used here, but
    ' is usually good to know (use custom args or INotifyPropertyChanged)
    Public Sub OnDataChanged(pname As String)
        ' fire shared datachanged event
        MyBase.BaseDataChanged(Me)
    End Sub

End Class

如何使用它们

现在您可以在不诉诸 Object 的情况下实现它们,订阅事件而不必对 get/set 属性使用反射:

 ' 'generic' object variable: DONT/CANT USE [New] w/ClaimBase
 Private myCB As ClaimBase
 ...
' set it as a Claim instance... 
'   This is perfectly legal because Claim is also a ClaimBase Type:
 myCB = New Claim

 ' hook up the event handler
 AddHandler myCB.DataChanged, AddressOf cb_DataChanged

您可以声明您的对象变量为ClaimBase,但您不能创建ClaimBase实例因为它是 abstract/MustInherit。由于事件是基础class的一部分,所以语法没有问题。表单级处理程序:

' Use standard (sender, e) signature
' (CA will object to other signatures:)
Private Sub cb_DataChanged(sender As Object, e As EventArgs)
    ' do change stuff here
    ...
End Sub

最重要的是,您可以直接引用属性:

cbObj.Name = "Ziggy"         ' will fire the event from the base class
cbObj.CurrentValue = 42      ' fires event from the Claim class

我添加了 ItemType 属性 以便您可以在 运行 时区分它们(即当您将鼠标悬停在 ClaimBase 对象变量上时)。 If/when 有类型特定的 properties/methods 可以访问,转换它(根据你所说的,现在不可能有​​任何这些):

If cbObj.ItemType = ClaimBoundType.Claim Then
    CType(cbObj, Claim).ClaimSomething = 5
End If    

还使用 ClaimBase 作为 Lists 的声明类型和方法签名也允许传递任何一种类型而不是将它们装箱(转换为 Object):

Private cbList As New List(Of ClaimBase)
   ...
' just an example of the declaration
Private Sub AddThingToList(cb As ClaimBase)
    cbList.Add(cb)
End Sub

我进入 INotifyProperty 并不是为了专注于继承,尽管它的基础知识在那个基础 class 中。它是实现 DataChanged/DirtyStatus 事件和检测的一种更系统的方法。