当前位置:  开发笔记 > 编程语言 > 正文

如何通过.NET/C#中的反射来引发事件?

如何解决《如何通过.NET/C#中的反射来引发事件?》经验,为你挑选了7个好方法。

我有一个第三方编辑器,基本上包括一个文本框和一个按钮(DevExpress ButtonEdit控件).我想做一个特定的击键(Alt+ Down)模拟点击按钮.为了避免一遍又一遍地写这个,我想创建一个通用的KeyUp事件处理程序,它将引发ButtonClick事件.不幸的是,控件中似乎没有引发ButtonClick事件的方法,所以...

如何通过反射从外部函数引发事件?



1> 小智..:

这是使用泛型的演示(省略错误检查):

using System;
using System.Reflection;
static class Program {
  private class Sub {
    public event EventHandler SomethingHappening;
  }
  internal static void Raise(this object source, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs
  {
    var eventDelegate = (MulticastDelegate)source.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(source);
    if (eventDelegate != null)
    {
      foreach (var handler in eventDelegate.GetInvocationList())
      {
        handler.Method.Invoke(handler.Target, new object[] { source, eventArgs });
      }
    }
  }
  public static void Main()
  {
    var p = new Sub();
    p.Raise("SomethingHappening", EventArgs.Empty);
    p.SomethingHappening += (o, e) => Console.WriteLine("Foo!");
    p.Raise("SomethingHappening", EventArgs.Empty);
    p.SomethingHappening += (o, e) => Console.WriteLine("Bar!");
    p.Raise("SomethingHappening", EventArgs.Empty);
    Console.ReadLine();
  }
}


我不认为有必要调用GetInvocationList().只需调用`eventDelegate`即可.
我注意到你创建了一个eventInfo局部变量,但是你从不对它做任何事情.它只是我还是可以删除第一行?

2> Jon Skeet..:

一般来说,你不能.将事件看作基本上成对AddHandler/ RemoveHandler方法(因为它基本上就是它们是什么).它们如何实施取决于课程.大多数WinForms控件都EventHandlerList用作它们的实现,但是如果它开始获取私有字段和键,你的代码将非常脆弱.

ButtonEdit控件是否公开了一个OnClick可以调用的方法?

脚注:实际上,事件可以 "提升"成员,因此EventInfo.GetRaiseMethod.但是,这从未被C#填充过,我也不相信它在框架中也是如此.



3> MichaelGG..:

你通常不能提出另一个类事件.事件实际上存储为私有委托字段,以及两个访问器(add_event和remove_event).

要通过反射来完成它,您只需要找到私有委托字段,获取它,然后调用它.



4> ChrisTTian66..:

我写了一个类的扩展,它实现了INotifyPropertyChanged以注入RaisePropertyChange 方法,所以我可以像这样使用它:

this.RaisePropertyChanged(() => MyProperty);

没有在任何基类中实现该方法.对于我的使用它是慢,但也许源代码可以帮助某人.

所以这里是:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Globalization;

namespace Infrastructure
{
    /// 
    /// Adds a RaisePropertyChanged method to objects implementing INotifyPropertyChanged.
    /// 
    public static class NotifyPropertyChangeExtension
    {
        #region private fields

        private static readonly Dictionary eventArgCache = new Dictionary();
        private static readonly object syncLock = new object();

        #endregion

        #region the Extension's

        /// 
        /// Verifies the name of the property for the specified instance.
        /// 
        /// The bindable object.
        /// Name of the property.
        [Conditional("DEBUG")]
        public static void VerifyPropertyName(this INotifyPropertyChanged bindableObject, string propertyName)
        {
            bool propertyExists = TypeDescriptor.GetProperties(bindableObject).Find(propertyName, false) != null;
            if (!propertyExists)
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                    "{0} is not a public property of {1}", propertyName, bindableObject.GetType().FullName));
        }

        /// 
        /// Gets the property name from expression.
        /// 
        /// The notify object.
        /// The property expression.
        /// a string containing the name of the property.
        public static string GetPropertyNameFromExpression(this INotifyPropertyChanged notifyObject, Expression> propertyExpression)
        {
            return GetPropertyNameFromExpression(propertyExpression);
        }

        /// 
        /// Raises a property changed event.
        /// 
        /// 
        /// The bindable object.
        /// The property expression.
        public static void RaisePropertyChanged(this INotifyPropertyChanged bindableObject, Expression> propertyExpression)
        {
            RaisePropertyChanged(bindableObject, GetPropertyNameFromExpression(propertyExpression));
        }

        #endregion

        /// 
        /// Raises the property changed on the specified bindable Object.
        /// 
        /// The bindable object.
        /// Name of the property.
        private static void RaisePropertyChanged(INotifyPropertyChanged bindableObject, string propertyName)
        {
            bindableObject.VerifyPropertyName(propertyName);
            RaiseInternalPropertyChangedEvent(bindableObject, GetPropertyChangedEventArgs(propertyName));
        }

        /// 
        /// Raises the internal property changed event.
        /// 
        /// The bindable object.
        /// The  instance containing the event data.
        private static void RaiseInternalPropertyChangedEvent(INotifyPropertyChanged bindableObject, PropertyChangedEventArgs eventArgs)
        {
            // get the internal eventDelegate
            var bindableObjectType = bindableObject.GetType();

            // search the base type, which contains the PropertyChanged event field.
            FieldInfo propChangedFieldInfo = null;
            while (bindableObjectType != null)
            {
                propChangedFieldInfo = bindableObjectType.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
                if (propChangedFieldInfo != null)
                    break;

                bindableObjectType = bindableObjectType.BaseType;
            }
            if (propChangedFieldInfo == null)
                return;

            // get prop changed event field value
            var fieldValue = propChangedFieldInfo.GetValue(bindableObject);
            if (fieldValue == null)
                return;

            MulticastDelegate eventDelegate = fieldValue as MulticastDelegate;
            if (eventDelegate == null)
                return;

            // get invocation list
            Delegate[] delegates = eventDelegate.GetInvocationList();

            // invoke each delegate
            foreach (Delegate propertyChangedDelegate in delegates)
                propertyChangedDelegate.Method.Invoke(propertyChangedDelegate.Target, new object[] { bindableObject, eventArgs });
        }

        /// 
        /// Gets the property name from an expression.
        /// 
        /// The property expression.
        /// The property name as string.
        private static string GetPropertyNameFromExpression(Expression> propertyExpression)
        {
            var lambda = (LambdaExpression)propertyExpression;

            MemberExpression memberExpression;

            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression)lambda.Body;
                memberExpression = (MemberExpression)unaryExpression.Operand;
            }
            else memberExpression = (MemberExpression)lambda.Body;

            return memberExpression.Member.Name;
        }

        /// 
        /// Returns an instance of PropertyChangedEventArgs for the specified property name.
        /// 
        /// 
        /// The name of the property to create event args for.
        /// 
        private static PropertyChangedEventArgs GetPropertyChangedEventArgs(string propertyName)
        {
            PropertyChangedEventArgs args;

            lock (NotifyPropertyChangeExtension.syncLock)
            {
                if (!eventArgCache.TryGetValue(propertyName, out args))
                    eventArgCache.Add(propertyName, args = new PropertyChangedEventArgs(propertyName));
            }

            return args;
        }
    }
}

我删除了原始代码的一些部分,因此扩展应该按原样工作,而不引用我的库的其他部分.但它并没有真正经过测试.

PS代码的某些部分是从别人那里借来的.对我感到羞耻,我忘记了从哪里得到它.:(



5> torial..:

通过反思提升事件,虽然我认为VB.NET中的答案,即在此之前的两个帖子将为您提供通用方法(例如,我将看一下VB.NET的灵感来源于引用不在同一类中的类型):

 public event EventHandler MyEventToBeFired;

    public void FireEvent(Guid instanceId, string handler)
    {

        // Note: this is being fired from a method with in the same
        //       class that defined the event (that is, "this").

        EventArgs e = new EventArgs(instanceId);

        MulticastDelegate eventDelagate =
              (MulticastDelegate)this.GetType().GetField(handler,
               System.Reflection.BindingFlags.Instance |
               System.Reflection.BindingFlags.NonPublic).GetValue(this);

        Delegate[] delegates = eventDelagate.GetInvocationList();

        foreach (Delegate dlg in delegates)
        {
            dlg.Method.Invoke(dlg.Target, new object[] { this, e });
        }
    }

    FireEvent(new Guid(),  "MyEventToBeFired");



6> Josh Kodroff..:

事实证明,我可以做到这一点并没有意识到:

buttonEdit1.Properties.Buttons[0].Shortcut = new DevExpress.Utils.KeyShortcut(Keys.Alt | Keys.Down);

但如果我不能,我将不得不深入研究源代码并找到引发事件的方法.

谢谢你们的帮助.



7> 小智..:

如果您知道控件是一个按钮,则可以调用其PerformClick()方法.我有其他事件像类似的问题OnEnter,OnExit.如果我不想为每种控件类型派生新类型,则无法引发这些事件.

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有