我在C#中有以下代码:
_timer.ElapsedTick += _somefunction1;
_timer.ElapsedTick += _somefunction2;
_timer.ElapsedTick += _somefunction3;
如何在不指定_somefunction的情况下调用订阅_timer.ElapsedTick的所有方法?在这个伪代码行附近。invoke(_timer.ElapsedTick);
我在C#中有以下代码:
_timer.ElapsedTick += _somefunction1;
_timer.ElapsedTick += _somefunction2;
_timer.ElapsedTick += _somefunction3;
如何在不指定_somefunction的情况下调用订阅_timer.ElapsedTick的所有方法?在这个伪代码行附近。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);
}
}
System.Timers.Timer
类,您需要像这样调用 FireEvent
:FireEvent(fromMe, "onIntervalElapsed", this, null);
- M.Babcocke
参数必须为 null,因为 ElapsedEventArgs
没有公共构造函数,而 ElapsedEventArgs.Empty
返回一个 EventArgs
。您也可以使用反射来实例化它,但我需要尝试一下才能确定。 - M.BabcockSystem.dll
反编译,并逆向了事件。像Reflector、ILSpy和JustDecompile这样的工具是.NET开发者工具箱里不可或缺的。 - M.Babcock我在搜索如何简单地启动事件时,最终来到了这里,该事件不具有任何自定义参数或其他内容。我知道OP的问题是不同的!但我想分享一下,希望能帮助到某些人。
简化版本:
var Handler = EventNameHere;
if (Handler != null)
{
Handler(this, EventArgs.Empty);
}
简化版:
EventNameHere?.Invoke(this, EventArgs.Empty);
public void RunAllFuncs()
{
_somefunction1();
_somefunction2();
_somefunction3();
}
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;
祝你好运!
MulticastDelegate
,然后获取它的调用列表,从那里你可以动态地单独调用每个方法。这个 MSDN问题的答案展示了如何做到这一点。 - M.Babcock在我之前的回答基础上,以下方法适用于我:
EventNameHere(null, EventArgs.Empty);
使用.NET 6(也可能适用于其他版本),以下通用方法可能会有所帮助。与其他答案类似,它使用反射来调用EventHandler:
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);
注意事项:
+=
将多个方法添加到事件处理程序中,则上述代码也适用。基于上面M.Babcock的答案,进行了一些轻微的反射方法更新。
用GetTypeInfo
和GetDeclaredField
替换了GetField
,用dlg.GetMethodInfo
替换了dlg.Method
。
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);
}
}
根据 ElapsedTick
事件的签名,您可以通过在声明该事件的类内部像调用方法一样调用它来触发事件。例如:
ElapsedTick(this, new EventArgs());
EventArgs.Empty
很好,因为它避免了不必要的分配。此外,您不能像那样从类的外部调用事件。 - Ed S.EventArgs.Empty
是一个不错的选择。 - BernardTimer
)。那么他如何利用这个建议呢?[回答我的问题:也许通过子类化Timer
,并添加一个包含此行代码的公共方法。这个答案需要展示如何做到这一点。] - ToolmakerSteve