2

我需要编写一个通用函数,用于删除SubFunc的所有事件处理程序(我需要通用函数才能与子例程和函数一起正常工作)。

...问题是我不知道该怎么做,我看到了声明代表的例子,但这并不像我精确的那样通用。

我读过这篇关于 CodeProject 的文章,但代码是 C# 我什么都看不懂:http: //www.codeproject.com/Articles/103542/Removing-Event-Handlers-using-Reflection

这是我唯一能做的:

Public Class Form1

    ' Call the function to remove all the handlers of "MySub"
    ' Clear_Handles(Of MySub)

    Private Function Clear_Handles(Of T)(ByVal MethodName As T)
        ' Code to remove all handlers(of "MethodName")
    End Function

    Private Sub MySub() Handles event1, event2, event3
        ' bla bla bla
    End Sub

End Class

感谢阅读。

更新

我试图将上面的代码转换为VB只是为了尝试但我不能使用它,我不知道如何使用它,这就是我所做的:

Public Class Form1

Private Sub MySub() Handles Button1.Click, Button2.click
    ' Do nothing
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ' Attempting to remove "Button1.Click" and "Button2.click" events of "MySub()"
    PSLib.cEventHelper.RemoveAllEventHandlers(MySub)
End Sub

End Class

...这是翻译后的代码,我不知道它是否有效,因为我不知道如何使用这些方法:

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.IO
Imports System.Management
Imports System.Reflection
Imports System.Text

Namespace PSLib
    '--------------------------------------------------------------------------------
    Public NotInheritable Class cEventHelper
        Private Sub New()
        End Sub
        Shared dicEventFieldInfos As New Dictionary(Of Type, List(Of FieldInfo))()

        Private Shared ReadOnly Property AllBindings() As BindingFlags
            Get
                Return BindingFlags.IgnoreCase Or BindingFlags.[Public] Or BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.[Static]
            End Get
        End Property

        '--------------------------------------------------------------------------------
        Private Shared Function GetTypeEventFields(t As Type) As List(Of FieldInfo)
            If dicEventFieldInfos.ContainsKey(t) Then
                Return dicEventFieldInfos(t)
            End If

            Dim lst As New List(Of FieldInfo)()
            BuildEventFields(t, lst)
            dicEventFieldInfos.Add(t, lst)
            Return lst
        End Function

        '--------------------------------------------------------------------------------
        Private Shared Sub BuildEventFields(t As Type, lst As List(Of FieldInfo))
            ' Type.GetEvent(s) gets all Events for the type AND it's ancestors
            ' Type.GetField(s) gets only Fields for the exact type.
            '  (BindingFlags.FlattenHierarchy only works on PROTECTED & PUBLIC
            '   doesn't work because Fieds are PRIVATE)

            ' NEW version of this routine uses .GetEvents and then uses .DeclaringType
            ' to get the correct ancestor type so that we can get the FieldInfo.
            For Each ei As EventInfo In t.GetEvents(AllBindings)
                Dim dt As Type = ei.DeclaringType
                Dim fi As FieldInfo = dt.GetField(ei.Name, AllBindings)
                If fi IsNot Nothing Then
                    lst.Add(fi)
                End If
            Next

        End Sub

        '--------------------------------------------------------------------------------
        Private Shared Function GetStaticEventHandlerList(t As Type, obj As Object) As EventHandlerList
            Dim mi As MethodInfo = t.GetMethod("get_Events", AllBindings)
            Return DirectCast(mi.Invoke(obj, New Object() {}), EventHandlerList)
        End Function

        '--------------------------------------------------------------------------------
        Public Shared Sub RemoveAllEventHandlers(obj As Object)
            RemoveEventHandler(obj, "")
        End Sub

        '--------------------------------------------------------------------------------
        Public Shared Sub RemoveEventHandler(obj As Object, EventName As String)
            If obj Is Nothing Then
                Return
            End If

            Dim t As Type = obj.[GetType]()
            Dim event_fields As List(Of FieldInfo) = GetTypeEventFields(t)
            Dim static_event_handlers As EventHandlerList = Nothing

            For Each fi As FieldInfo In event_fields
                If EventName <> "" AndAlso String.Compare(EventName, fi.Name, True) <> 0 Then
                    Continue For
                End If

                ' After hours and hours of research and trial and error, it turns out that
                ' STATIC Events have to be treated differently from INSTANCE Events...
                If fi.IsStatic Then
                    ' STATIC EVENT
                    If static_event_handlers Is Nothing Then
                        static_event_handlers = GetStaticEventHandlerList(t, obj)
                    End If

                    Dim idx As Object = fi.GetValue(obj)
                    Dim eh As [Delegate] = static_event_handlers(idx)
                    If eh Is Nothing Then
                        Continue For
                    End If

                    Dim dels As [Delegate]() = eh.GetInvocationList()
                    If dels Is Nothing Then
                        Continue For
                    End If

                    Dim ei As EventInfo = t.GetEvent(fi.Name, AllBindings)
                    For Each del As [Delegate] In dels
                        ei.RemoveEventHandler(obj, del)
                    Next
                Else
                    ' INSTANCE EVENT
                    Dim ei As EventInfo = t.GetEvent(fi.Name, AllBindings)
                    If ei IsNot Nothing Then
                        Dim val As Object = fi.GetValue(obj)
                        Dim mdel As [Delegate] = TryCast(val, [Delegate])
                        If mdel IsNot Nothing Then
                            For Each del As [Delegate] In mdel.GetInvocationList()
                                ei.RemoveEventHandler(obj, del)
                            Next
                        End If
                    End If
                End If
            Next
        End Sub

    End Class
End Namespace

更新

这是我会做的一个例子:

private sub form1_shown() handles me.shown
    RemoveAll_EventHandlers(of MyMethod)
    ' So it will remove: button1.click, button2.click, button3.click 
end sub

Private sub MyMethod() handles button1.click, button2.click, button3.click
   ' Nothing to do here. . . 
end sub

public sub RemoveAll_EventHandlers(of T)(byval MethodName as T)
      For each evt as event in MethodName : removehandler control.event, addres of(T) 
end sub

更新:

你想象的另一个例子......:

Private Sub RemoveAll_EventHandlers(Of T)(ByVal MethodName As T)
    For Each ctrl As Control In Me.Controls
        For Each evt As EventHandler In Control
            RemoveHandler ctrl.evt, addresof(T)
        Next
    Next
End Sub

private sub form1_shown() handles me.shown
    RemoveAll_EventHandlers(of MyMethod)
    ' So it will remove: button1.click, button2.click, button3.click 
end sub

Private sub MyMethod() handles button1.click, button2.click, button3.click
   ' Nothing to do here. . . 
end sub
4

2 回答 2

3

这是在 C# 中,但它是通用的。据我了解,您的课程中有很多事件。您将使用相同的方法订阅多个事件。您想从它订阅的所有事件中删除该方法。我写了一个快速测试,可能是一些极端情况

    public static class EventUtility
        {
            public static void Unsubscribe(object obj, Delegate method)
            {
                Type type = obj.GetType();
                foreach (EventInfo eventInfo in type.GetEvents(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    FieldInfo field = type.GetField(eventInfo.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    Delegate eventDelegate = field.GetValue(obj) as Delegate;
                    foreach (Delegate subscriber in eventDelegate.GetInvocationList())
                    {
                        if (subscriber.Method == method.Method)
                        {
                            eventInfo.RemoveEventHandler(obj, subscriber);
                        }
                    }
                }
            }
        }

        public class TestClass
        {
            public event EventHandler EventA;
            public event EventHandler EventB;
            public event EventHandler EventC;

            public void A()
            {
                Console.WriteLine("A Start");
                EventA(this, EventArgs.Empty);
                Console.WriteLine("A End");
            }

            public void B()
            {
                Console.WriteLine("B Start");
                EventB(this, EventArgs.Empty);
                Console.WriteLine("B End");
            }
            public void C()
            {
                Console.WriteLine("C Start");
                EventC(this, EventArgs.Empty);
                Console.WriteLine("C End");
            }

        }

        class Program
        {
            public static void test2()
            {
                TestClass t = new TestClass();
                t.EventA += Handler1;
                t.EventB += Handler1;
                t.EventC += Handler1;

                t.EventA += Handler2;
                t.EventB += Handler2;
                t.EventC += Handler2;

                t.EventA += Handler3;
                t.EventB += Handler3;
                t.EventC += Handler3;

                t.A();
                t.B();
                t.C();

                EventUtility.Unsubscribe(t, new EventHandler(Handler2));

                t.A();
                t.B();
                t.C();
            }


            private static void Handler1(object instance, EventArgs args)
            {
                Console.WriteLine("handler1 invoked.");
            }

            private static void Handler2(object instance, EventArgs args)
            {
                Console.WriteLine("handler2 invoked.");
            }

            private static void Handler3(object instance, EventArgs args)
            {
                Console.WriteLine("handler3 invoked.");
            }

            static void Main()
            {
                test2();
            }
}
于 2013-06-26T23:42:20.647 回答
1

尝试这样的事情:

For Each b As Button in MySub.Controls.OfType(Of Button)

    'This should remove all the handlers for each button
    PSLib.cEventHelper.RemoveAllEventHandlers(b)

    'Or like this to just remove just the Click handler for each button
    PSLib.cEventHelper.RemoveEventHandler(b, "Click")

Next

建议先使用原版。让您的前端与它正确连接。然后进行翻译。这样你就有了一个基线并知道它应该如何工作。

更新:

我认为部分混淆在于术语的滥用。在您的更新中 MyMethod 是一个处理程序,它不包含处理程序,但它可以处理多个事件。Button1.Click 是一个事件。Button1 是一个按钮。因此,使用正确的术语,您似乎想要删除由特定处理程序(MyMethod)处理的所有事件(Button1.Click、Button2.Click 等)。

这是一种方法,假设所有控件都相同:

Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    For Each b As Control In Me.Controls
        If b.Name.Contains("3") OrElse b.Name.Contains("4") Then
            b.Tag = New KeyValuePair(Of String, Boolean)("1", True)
            AddHandler b.Click, AddressOf Button_Click1
        Else
            b.Tag = New KeyValuePair(Of String, Boolean)("2", True)
            AddHandler b.Click, AddressOf Button_Click2
        End If
    Next
End Sub
Private Sub RemoveEvents(Handler As String)
    For Each b As Control In Me.Controls
        Dim TempTag As KeyValuePair(Of String, Boolean) = DirectCast(b.Tag, KeyValuePair(Of String, Boolean))
        If TempTag.Key = Handler AndAlso TempTag.Value = False Then
            Select Case Handler
                Case "1"
                    RemoveHandler b.Click, AddressOf Button_Click1
                Case "2"
                    RemoveHandler b.Click, AddressOf Button_Click2
            End Select
        End If
    Next
End Sub
Private Sub Button_Click1(sender As Object, e As EventArgs)
    'do stuff
End Sub
Private Sub Button_Click2(sender As Object, e As EventArgs)
    'do stuff
End Sub

将标记的 keyvaluepair 值设置为 false,以使子例程从该控件的单击事件中删除处理程序

于 2013-06-10T17:59:36.477 回答