如何手动调用事件
本文关键字:事件 调用 何手动 | 更新日期: 2023-09-27 18:19:39
我在 C# 中有以下行:
_timer.ElapsedTick += _somefunction1;
_timer.ElapsedTick += _somefunction2;
_timer.ElapsedTick += _somefunction3;
如何调用订阅_timer的所有方法。已过滴答而不指定_somefunction?沿着这条伪线的某个地方
invoke(_timer.ElapsedTick);
不能调用由其他类型拥有的事件。 事件只能从声明它的类的内部调用。
可以使用传统的 C# 来完成吗?否(如前所述(。但是使用反射是可能的。下面是一些基于此 MSDN 论坛主题的答案的测试代码:
class InvokeFromMe
{
public event EventHandler RaiseMe;
}
class Program
{
static void Main(string[] args)
{
var fromMe = new InvokeFromMe();
fromMe.RaiseMe += fromMe_RaiseMe;
fromMe.RaiseMe += fromMe_RaiseMe1;
fromMe.RaiseMe += fromMe_RaiseMe2;
FireEvent(fromMe, "RaiseMe", null, EventArgs.Empty);
}
static void fromMe_RaiseMe(object sender, EventArgs e)
{
Console.WriteLine("Event Handler 0 Raised");
}
static void fromMe_RaiseMe1(object sender, EventArgs e)
{
Console.WriteLine("Event Handler 1 Raised");
}
static void fromMe_RaiseMe2(object sender, EventArgs e)
{
Console.WriteLine("Event Handler 2 Raised");
}
public static void FireEvent(object onMe, string invokeMe, params object[] eventParams)
{
MulticastDelegate eventDelagate =
(MulticastDelegate)onMe.GetType().GetField(invokeMe,
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic).GetValue(onMe);
Delegate[] delegates = eventDelagate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(dlg.Target, eventParams);
}
}
}
更新
我不熟悉System.Timer.Timer
类,所以我不确定与我提供的示例有什么不同。你也许可以尝试这样的事情:
public static void FirePublicEvent(object onMe, string invokeMe, params object[] eventParams)
{
MulticastDelegate eventDelagate =
(MulticastDelegate)onMe.GetType().GetField(invokeMe,
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public).GetValue(onMe);
Delegate[] delegates = eventDelagate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(dlg.Target, eventParams);
}
}
好吧,我在这里结束了关于如何简单地启动事件的内容,它没有任何自定义参数或任何东西。我知道OP问题是不同的!但我想分享,我希望它能帮助到某人。
非简化版本:
var Handler = EventNameHere;
if (Handler != null)
{
Handler(this, EventArgs.Empty);
}
简化版本:
EventNameHere?.Invoke(this, EventArgs.Empty);
您可以创建一个运行所有这些的函数:
public void RunAllFuncs()
{
_somefunction1();
_somefunction2();
_somefunction3();
}
这是一个解决方法,您无法循环遍历已用事件。 但是要全部调用它们,请让计时器完成工作。 此代码用于 System.Timer,不确定您使用的是哪个计时器。
假设计时器已启用:
int interval = timer.Interval;
ElapsedEventHandler handler = null;
handler = (s,e) =>
{
timer.Interval = interval; // put interval back to original value
timer.Elapsed -= handler;
};
timer.Elapsed += handler;
timer.Interval = 1; // 1 millisecond, pretty much going to fire right now (as soon as you let it)
类似的事情将触发事件,但您的原始间隔将重新启动。 如果你想保持原始的即时报价模式,你可能必须在那里做一些数学运算。
我认为您可能需要使用InvokeMember:
public void GetMethod(string methodName){
var args = new Object[] { [INSERT ARGS IF NECESSARY--SET TO NULL OTHERWISE] };
try
{
var t = new [INSERT NAME OF CLASS THAT CONTAINS YOUR METHOD]();
Type typeInfo = t.GetType();
var result = typeInfo.InvokeMember(methodName, BindingFlags.InvokeMethod, null, t, args);
return result;
}
catch (Exception ex)
{
return ex;
}
}
然后这样称呼它:
_timer.ElapsedTick += GetMethod("[INSERT METHOD NAME AS STRING]");
请务必包括以下内容:
using System.Reflection;
祝你好运!
基于M.Babcock上面的回答,有一些轻微的反射方法更新。
将GetField
替换为GetTypeInfo
和GetDeclaredField
,dlg.Method
替换为dlg.GetMethodInfo
。
using System.Reflection;
...
public static void FireEvent(this object onMe, string invokeMe, params object[] eventParams)
{
TypeInfo typeInfo = onMe.GetType().GetTypeInfo();
FieldInfo fieldInfo = typeInfo.GetDeclaredField(invokeMe);
MulticastDelegate eventDelagate = (MulticastDelegate)fieldInfo.GetValue(onMe);
Delegate[] delegates = eventDelagate.GetInvocationList();
foreach (Delegate dlg in delegates)
{
dlg.GetMethodInfo().Invoke(dlg.Target, eventParams);
}
}
对于 .NET 6(也可以与其他版本一起使用(,以下泛型方法可能会有所帮助。与其他答案一样,它使用反射来调用事件处理程序:
private void InvokeEvent<T, TEvent>(T target, string eventName, TEvent @event)
{
// some reflection magic to fire an event from outside the target class:
if(target == null)
{
throw new ArgumentNullException(nameof(target));
}
Type targetType = target.GetType();
FieldInfo? fi = targetType.GetField(eventName, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
if(fi != null)
{
EventHandler<TEvent>? eventHandler = fi.GetValue(target) as EventHandler<TEvent>;
if(eventHandler != null)
{
eventHandler.Invoke(this, @event);
}
}
}
然后可以像这样调用它:
const string eventName = nameof(Algorithm.Received);
DomainEvent @event = new DomainEvent(payload);
InvokeEvent(targetObject, eventName, @event);
笔记:
- 如果将多个方法与
+=
添加到事件处理程序,则上面的代码也有效。 - 如果要在产品中使用此代码,则可能需要考虑更多的错误处理,例如参数检查或在找不到特定事件处理程序时引发异常。
- 对于 .NET 的每个主要版本,可能需要更改使用反射的代码。由于.NET 6是跨平台运行的Windows,Linux,MacOS等。我怀疑更改破坏上述代码的可能性降低了,尽管不是零。
- 我们在自动测试套件中使用此代码。如果使用反射的代码确实发生了变化,我们只需要更改此方法。
基于我面前的答案,以下内容对我有用:
EventNameHere(null, EventArgs.Empty);
根据 ElapsedTick
事件的签名,可以通过从声明它的类内部像方法一样调用事件来触发事件。例如:
ElapsedTick(this, new EventArgs());
您只能从声明它的类内部调用事件。如果您编写了计时器类,则可以像这样调用它:
this.ElapsedTick(this, eventArgs);
在计时器类之外,您可以执行以下操作来获得类似的行为:
delegate void delegateSample(object sender, EventArgs e);
delegateSample d;
d += new delegateSample(_somefunction1);
d += new delegateSample(_somefunction2);
d(this, EventArgs.Empty);
delegateSample 实际上应该是 ElapsedTick 事件的类型,而不是声明另一个委托类型。我只是不知道那个代表的名字。只需确保将每个函数添加到 ElapsedTick 事件和您声明的委托变量,然后您就可以从 Timer 类之外的委托调用它们。