DotNet / 编程技术 · 2022年2月26日

清除对像所有事件的代码示例

‘下面的方法是清除了Button按钮关联的所有Click事件,验证有效  
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonX2.Click

        Dim _PropertyInfo As PropertyInfo = GetType(Button).GetProperty("Events", BindingFlags.Instance Or BindingFlags.NonPublic)
        If _PropertyInfo Is Nothing Then Exit Sub
        Dim _EventList As EventHandlerList = CType(_PropertyInfo.GetValue(ButtonX1, Nothing), EventHandlerList)
        If _EventList Is Nothing Then Exit Sub
        Dim _FieldInfo As FieldInfo = (GetType(Control)).GetField("EventClick", BindingFlags.Static Or BindingFlags.NonPublic)
        If _FieldInfo Is Nothing Then Exit Sub
        Dim _ObjectDelegate As [Delegate] = _EventList(_FieldInfo.GetValue(ButtonX1))
        If _ObjectDelegate Is Nothing Then Exit Sub

        For Each eh As [Delegate] In _ObjectDelegate.GetInvocationList()
            RemoveHandler ButtonX1.Click, CType(eh, EventHandler)
        Next
    End Sub

_ObjectDelegate.GetInvocationList()   ‘返回委托的调用列表

参考Control为事件采用EventHandlerList的代码分析

class MyControl1
{
    // 即使没有订阅,每个事件作为类成员需要4个字节(的引用,没有订阅时为null)。
    // Winform的控件至少有70个事件, 这就要280个字节,且大部分事件都没有被用到。
    // 就是说每个UI上的控件,Lable,Button,TextBox,等等,每个都可能浪费约300个字节。
    public event EventHandler Initializing;
    public event EventHandler Initialized;
    // ...
    public event EventHandler MouseDown;
    public event EventHandler MouseUp;
    public event EventHandler MouseMove;
    public event EventHandler MouseClick;
    public event EventHandler MouseDoubleClick;
    public event EventHandler KeyDown;
    public event EventHandler KeyUp;
    // ...
}
 
class MyControl2
{
    // 一个EventHandlerList初始只要4个字节(的引用)
    private EventHandlerList _events;
    protected EventHandlerList Events
    {
        get 
        
            if (_events == null) _events = new EventHandlerList();
            return _events;
        }
    }
 
    public event EventHandler Initializing
    {
        add { Events.AddHandler("Initializing", value); }  // 按需增加
        remove { Events.RemoveHandler("Initializing", value); }
    }
 
    public event EventHandler Initialized
    {
        add { Events.AddHandler("Initialized", value); }
        remove { Events.RemoveHandler("Initialized", value); }
    }
    // ...
}
注,除非像Control那样有大量的事件,一般不需要用到EventHandlerList,往往代码简洁更重要。

以上代码有一个局限,就是控件必须从Control继承,且内部事件变量名称必须以Event+事件方式命名,如果内部事件变量名称被混淆,将失效

下面的代码,在上面的代码基础上,优化后,可以无须从Control继承,但依然不能被混淆,混淆后还是会失效
Public Sub removeAllEvents(pObject As Object, pEventName As String)
        Dim pBindingFlags = BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Static
        Dim pFieldBindingFlags = BindingFlags.NonPublic Or BindingFlags.Static Or BindingFlags.IgnoreCase

        Dim pPropertyInfo = pObject.GetType().GetProperty("Events", pBindingFlags)
        For Each pType In getBaseTypes(pObject)
            Dim pFieldInfo = pType.GetField("Event" + pEventName, pFieldBindingFlags)
            If pFieldInfo Is Nothing Then
                Continue For
            End If
            Dim pField As [Delegate] = CType(pFieldInfo.GetValue(pObject), [Delegate])
            If pField IsNot Nothing Then
                Dim pObjectDelegate = pEventList(pField)
                If pObjectDelegate IsNot Nothing Then
                    For Each pInvocatedDelegate In pObjectDelegate.GetInvocationList()
                        pType.GetEvent(pEventName).RemoveEventHandler(pObject, pInvocatedDelegate)  
                        ' 或者.GetRemoveMethod(True).Invoke(pObject, New Object() {pInvocatedDelegate})
                    Next
                    Return
                End If
            End If
        Next

    End Sub

    Private Function getBaseTypes(pObj As Object) As Type()
        If pObj Is Nothing Then
            Return New Type() {}
        End If
        Dim pObjType = pObj.GetType()
        pObjType = pObjType.BaseType()
        Dim pTypes As New List(Of Type)
        Do While pObjType IsNot Nothing
            pTypes.Add(pObjType)
            pObjType = pObjType.BaseType()
        Loop
        Return pTypes.ToArray()
    End Function

最新电影,电视剧,尽在午夜剧场

电影电视剧午夜不寂寞