Based on some crude testing, I
suspect the problem is actually the other way round: you
can create the delegate when the method
hasn't been overridden, and you
can't create the delegate when the method
has been overridden.
Class Foo
Protected Overridable Sub Click()
Console.WriteLine("Foo.Click")
End Sub
End Class
Class Bar : Inherits Foo
End Class
Class Baz : Inherits Foo
Protected Overrides Sub Click()
Console.WriteLine("Baz.Click")
MyBase.Click()
End Sub
End Class
Delegate Sub MyDelegate(ByVal instance As Foo)
Function CreateDelegate(ByVal instance As Foo) As MyDelegate
Dim myType As Type = instance.GetType()
Dim myInfo As MethodInfo = myType.GetMethod("Click", BindingFlags.NonPublic Or BindingFlags.Instance)
Return DirectCast([Delegate].CreateDelegate(GetType(MyDelegate), myInfo, True), MyDelegate)
End Function
Sub Main
Dim f As New Foo()
CreateDelegate(f).Invoke(f)
f = New Bar()
CreateDelegate(f).Invoke(f)
f = New Baz()
CreateDelegate(f).Invoke(f)
End Sub
The reason is fairly simple. When the method has been overridden, the
GetMethod
returns a
MethodInfo
representing the overridden version of the method. That overridden version can only be called on an instance of the class where the override is declared. But you are trying to bind it to a delegate that can be called on
any instance of the base class.
So, for example, you could create a delegate to call
Baz.Click
, but pass in an instance of
Bar
instead.
The simple solution is to retrieve the
MethodInfo
for the base method:
Function CreateDelegate(ByVal instance As Foo) As MyDelegate
Dim myType As Type = GetType(Foo)
Dim myInfo As MethodInfo = myType.GetMethod("Click", BindingFlags.NonPublic Or BindingFlags.Instance)
Return DirectCast([Delegate].CreateDelegate(GetType(MyDelegate), myInfo, True), MyDelegate)
End Function
Invoking the delegate will still call the overridden version:
f = New Baz()
CreateDelegate(f).Invoke(f)
So your code would become:
Private Sub Button_Down(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
RepeatControl = sender
RepeatDelegate = Nothing
Dim myType As Type = GetType(Control)
Dim myInfo As System.Reflection.MethodInfo = myType.GetMethod("OnClick", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
If myInfo IsNot Nothing Then RepeatDelegate = [Delegate].CreateDelegate(GetType(delegate_OnClick), myInfo, False)
RepeatDelayCounter = 0
End Sub
Alternatively, you could create a closed instance delegate, and you wouldn't need the
RepeatControl
member at all:
Private RepeatDelayCounter As Integer = 0
Private Delegate Sub delegate_OnClick(ByVal e As System.EventArgs)
Private RepeatDelegate As delegate_OnClick = Nothing
Private Sub Button_Down(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
RepeatDelegate = Nothing
Dim myType As Type = GetType(Control)
Dim myInfo As System.Reflection.MethodInfo = myType.GetMethod("OnClick", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
If myInfo IsNot Nothing Then RepeatDelegate = [Delegate].CreateDelegate(GetType(delegate_OnClick), sender, myInfo, False)
RepeatDelayCounter = 0
End Sub