如何使用反射将匿名操作绑定为新的事件处理程序



我正在为Windows Forms编写一些单元测试,到目前为止已经能够弄清楚如何使用Reflection设置私有控件的属性并调用它们的方法。但是我被困在如何关联一个内联lambda附加自己到一个控件上发生的事件,在这种情况下,DataGridView的DataSourceChanged事件。

public static void ObserveGrid(this Form form, string controlName, Action action)
{
    var controls = form.Controls.Find(controlName, true);
    if (controls.Any())
    {
        var control = controls[0] as DataGridView;
        if (control != null)
        {
            EventInfo ei = typeof(DataGridView).GetEvent("DataSourceChanged");
            if (ei != null)
            {
                ei.AddEventHandler(control, Delegate.CreateDelegate(ei.EventHandlerType, control, action.Method ));
            }
        }
    }
}

我希望这样称呼它:

var monitor = new Mutex();
form.ObserveGrid("dataGridView1",
    () =>
    {
        Trace.WriteLine("Releasing mutex.");
        monitor.ReleaseMutex();
    });
var sw = new Stopwatch();
form.ClickButton("btnSearch", sw);
monitor.WaitOne();
sw.Stop();

在执行过程中,我得到一个错误:

无法绑定到目标方法,因为它的签名或安全性透明度与委托类型不兼容。

在这种情况下我做错了什么?

更新:

使用这个伟大的帖子,我已经改变了我的扩展类如下:

    public static void ObserveGrid(this Form form, string controlName, Action<object,object> action)
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0] as DataGridView;
            if (control != null)
            {
                EventInfo ei = typeof(DataGridView).GetEvent("DataSourceChanged");
                if (ei != null)
                {
                    Delegate handler = ConvertDelegate(action, ei.EventHandlerType);
                    ei.AddEventHandler(control, handler);
                }
            }
        }
    }
    public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
    {
        return Delegate.CreateDelegate(
            targetDelegateType,
            originalDelegate.Target,
            originalDelegate.Method);
    }

然而,我得到了另一个错误,这次是关于从非同步线程释放互斥锁:

释放互斥锁。System.Reflection.TargetInvocationException:异常已被调用的目标抛出。——>系统。ApplicationException:对象同步方法从未同步的代码块中调用。

更新2

为SemaphoreSlim交换互斥锁解决了同步问题。

我最后是这样做的:

首先,Extensions类:
public static class Extensions
{
    #region Public Methods and Operators
    public static Stopwatch ClickButton(this Form form, string controlName)
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0] as Button;
            if (control != null)
            {
                MethodInfo mi = typeof(Button).GetMethod("OnClick", BindingFlags.NonPublic | BindingFlags.Instance);
                var stopWatch = Stopwatch.StartNew();
                mi.Invoke(
                    control,
                    new object[]
                    {
                        EventArgs.Empty
                    });
                return stopWatch;
            }
        }
        throw new ApplicationException("Control not found or of invalid Type");
    }
    public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
    {
        return Delegate.CreateDelegate(targetDelegateType, originalDelegate.Target, originalDelegate.Method);
    }
    public static object GetControlProperty<T>(this Form form, string controlName, string propertyName) where T : class
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0];
            PropertyInfo pi = typeof(T).GetProperty(propertyName);
            return pi.GetValue(control, null);
        }
        throw new ApplicationException("Control not found or of invalid Type");
    }
    public static void ObserveControlEvents<T>(this Form form, string controlName, string eventName, Action<object, object> action) where T : class
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0] as T;
            if (control != null)
            {
                EventInfo ei = typeof(T).GetEvent(eventName);
                if (ei != null)
                {
                    Delegate handler = ConvertDelegate(action, ei.EventHandlerType);
                    ei.AddEventHandler(control, handler);
                }
            }
        }
    }
    public static void ObserveGrid(this Form form, string controlName, string eventName, Action<object, object> action)
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0] as DataGridView;
            if (control != null)
            {
                EventInfo ei = typeof(DataGridView).GetEvent(eventName);
                if (ei != null)
                {
                    Delegate handler = ConvertDelegate(action, ei.EventHandlerType);
                    ei.AddEventHandler(control, handler);
                }
            }
        }
    }
    public static void SetControlProperty<T>(this Form form, string controlName, string propertyName, object value) where T : class
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0];
            PropertyInfo pi = typeof(T).GetProperty(propertyName);
            pi.SetValue(control, value, null);
        }
    }
    public static void SetFormProperty(this Form form, string controlName, object value)
    {
        var controls = form.Controls.Find(controlName, true);
        if (controls.Any())
        {
            var control = controls[0];
            PropertyInfo pi = typeof(Control).GetProperty("Text");
            pi.SetValue(control, value, null);
        }
    }
    #endregion
}

现在,在我的单元测试中,我可以创建表单,设置值,触发和观察事件:

[TestFixture]
public class FormsTests
{
    #region Public Methods and Operators
    [TestCase(null, null, null, 1000)]
    [TestCase("Kim", null, null, 500)]
    [TestCase("Kim", null, "Akers", 250)]
    [TestCase("Kim", "B", "Abercrombie", 100)]
    public void InsuredSearcherResponseTimeWithReflectionTest(string firstName, string middleName, string lastName, long milliseconds)
    {
        var monitor = new SemaphoreSlim(1);
        monitor.Wait();
        var form = new Insured();
        form.SetControlProperty<TextBox>("tbFirstName", "Text", firstName);
        form.SetControlProperty<TextBox>("tbMiddleName", "Text", middleName);
        form.SetControlProperty<TextBox>("tbLastName", "Text", lastName);
        form.ObserveControlEvents<DataGridView>(
            "dataGridView1",
            "DataSourceChanged",
            (sender, args) =>
            {
                Trace.WriteLine("Occured in delegate");
                monitor.Release();
            });
        Trace.WriteLine("Executing");
        var sw = form.ClickButton("btnSearch");
        monitor.Wait();
        sw.Stop();
        Trace.WriteLine(String.Format("Row count was {0} took {1}ms to process", form.GetControlProperty<DataGridView>("dataGridView1", "RowCount"), sw.ElapsedMilliseconds));
        Assert.IsTrue(sw.ElapsedMilliseconds < milliseconds);
    }
    [TestFixtureSetUp]
    public void TestFixtureSetup()
    {
        var monitor = new SemaphoreSlim(1);
        monitor.Wait();
        var form = new Insured();
        form.ObserveControlEvents<DataGridView>(
            "dataGridView1",
            "DataSourceChanged",
            (sender, args) =>
            {
                Trace.WriteLine("Occured in delegate");
                monitor.Release();
            });
        form.ClickButton("btnSearch");
        monitor.Wait();
    }
}

我希望这也能帮助到别人。

试试下面的定义:

public static class Xtd
{
    public static void AddEventEasy(this object component, EventInfo eventInfo, Delegate eventAction)
    {
        Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
        {
            return Delegate.CreateDelegate(
                targetDelegateType,
                originalDelegate.Target,
                originalDelegate.Method);
        }
        eventInfo.AddEventHandler(component, ConvertDelegate(eventAction, eventInfo.EventHandlerType));
    }
}

最新更新