从methodinfo创建委托(2) [英] Create a delegate from methodinfo (2)

查看:56
本文介绍了从methodinfo创建委托(2)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的情况是我想要一个来自Control的方法的委托。



为此,我使用如下代码:

My Situation is that I want to have a Delegate to a method from a Control.

For that I use the code as follows :

Private RepeatDelayCounter As Integer = 0
Private RepeatControl As Control = Nothing
Private Delegate Sub delegate_OnClick(instance As Control, 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)
    RepeatControl = sender
    RepeatDelegate = Nothing

    Dim myType As Type = RepeatControl.GetType
    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



你应该知道这只是一部分复杂类的(但相关的)。



如果启动Button_Down方法的Control使用覆盖版本,则此代码正常工作(我得到一个委托) OnClick方法。



如果我不使用自定义控件但使用标准控件(例如按钮),我会得到正确的MethodInfo,因为这个Control里面有一个OnClick-Method,但是我找不到一个委托。

错误消息我得到指向Binding-Flags,但是这些与我的那些完全相同自定义控件。所以我认为它来自标准-Control的OnClick方法的Protected属性。



所以...我需要的是一个有用的建议如何解决这个问题 - 因为我想了解它... ;-)



我尝试过的事情:



见上文...


You should know that this is only a part (but the relevant one) of a complex class.

This code works properly (I get a Delegate) if the Control which starts the Button_Down-method uses a overrided Version of the OnClick-method.

If I don't use a customized Control but a "Standard"-Control (for example a Button) I get the right MethodInfo, because this Control has a OnClick-Method inside, but I can't get a Delegate.
The Error-Message I get points to the Binding-Flags, but these are completely the same as those from my custom Control. So I think it comes from the Protected-attribute of the OnClick-method from the "Standard"-Control.

So ... what I need is a useful advice how to solve this - because I want to understand it ... ;-)

What I have tried:

see above ...

推荐答案

根据一些原油测试,我怀疑问题实际上是另一种方式:当没有被覆盖时,你可以创建委托,而你不能创建委托当方法已被重写时。

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) 
    ' OK: Prints "Foo.Click"
    
    f = New Bar()
    CreateDelegate(f).Invoke(f) 
    ' OK: Prints "Foo.Click"
    
    f = New Baz()
    CreateDelegate(f).Invoke(f) 
    ' ArgumentException: 
    ' Cannot bind to the target method because its signature or security 
    ' transparency is not compatible with that of the delegate type.
End Sub





原因很简单。重写该方法后, GetMethod 将返回 MethodInfo ,表示该方法的重写版本。只能在声明覆盖的类的实例上调用该重写版本。但是你试图将它绑定到一个可以在基类的任何实例上调用的委托。



所以,例如,您可以创建一个委托来调用 Baz.Click ,但是传入一个 Bar 的实例。



简单的解决方案是检索基本方法的 MethodInfo



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



调用delegate仍会调用被覆盖的版本:


Invoking the delegate will still call the overridden version:

f = New Baz()
CreateDelegate(f).Invoke(f) 
' OK: Prints "Baz.Click", then "Foo.Click"





所以你的代码会变成:



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





或者,你可以创建一个封闭的实例委托,你根本不需要 RepeatControl 成员:



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


这篇关于从methodinfo创建委托(2)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆