gpt4 book ai didi

c# - 根据规范监听事件并调用回调?

转载 作者:行者123 更新时间:2023-12-04 03:48:40 25 4
gpt4 key购买 nike

我目前正在构建一个自定义任务管理器,我想知道是否可以告诉任务管理器监听特定事件(下面的 OnSomething),然后在发生时调用回调方法任务引发该事件。但是,在精神上我看不出如何监听在基类级别不存在的事件。例如,我有一个基类,其中包含有关名为 CustomTask 的任务的基本信息:

public abstract class CustomTask {
public bool IsRunning { get; private set; } = false;
public void Start() {
IsRunning = true;
DoSomething();
IsRunning = false;
}
protected abstract void DoSomething();
}

为了 SO 读者的缘故,我简化了定义,但您已经了解了它的要点。它包含基本的细节,一些启动和取消的方法,提供基本的状态管理(这里简化了IsRunning)等。

然后我有从 CustomTask 派生的自定义任务,在这种情况下,让我们关注一个名为 CustomTaskA 的示例任务。它包含一个名为 OnSomething 的事件的定义,某个地方的某个人可能想要监听该事件:

public sealed class CustomTaskA : CustomTask {
protected override void DoSomething() => RaiseOnSomething(this, new EventArgs());
public event EventHandler<EventArgs> OnSomething;
private void RaiseOnSomething(object sender, EventArgs e) => OnSomething?.Invoke(sender, e);
}

现在,CustomTaskManager 注册任务,通过 Guid 跟踪它们,管理它们等等,但为了简单起见:

public sealed class CustomTaskManager {
// Singleton setup.
private static CustomTaskManager _instance = new CustomTaskManager();
public static CustomTaskManager Instance {
get {
// Simplified for SO.
if (_instance == null)
_instance = new CustomTaskManager();

return;
}
}

// Collection of tasks.
private Dictionary<Guid, CustomTask> _tasks = new Dictionary<Guid, CustomTask>();

// Register and start a task.
public bool TryRegisterAndStartTask(CustomTask task, out Guid taskId) {
taskId = Guid.Empty;
try {
// Register task.
taskId = Guid.NewGuid();
_tasks.Add(taskId, task);

// Listen for events.

// Start task.
task.Start();
} catch (Exception e) {
// Log exception.
}

return false;
}
}

在注册和启动任务时,我想告诉任务管理器我想监听OnSomething,如果OnSomething被调用,我想要任务管理器调用方法 OnSomethingWasRaised。例如:

TaskManager.Instance.TryRegisterAndStartTask(task, out Guid taskId, task.OnSomething, OnSomethingWasRaised);

private static void OnSomethingWasRaised(object sender, EventArgs e) {
Console.WriteLine("Woohoo!");
}

我知道指定和调用回调方法是完全可能的,并且通过反射监听事件是合理的。


有没有办法(使用或不使用反射)监听在派生对象上定义的指定事件,然后调用指定的回调方法?

注意:请原谅任何语法错误,因为我手写了这些片段以尽量减少它们。

最佳答案

这样的(提议的)方法存在问题:

TryRegisterAndStartTask(task, out Guid taskId, task.OnSomething, OnSomethingWasRaised);

是你不能将事件作为参数传递,或将其存储在变量中,因为事件只是一组两个方法(添加和删除),就像属性是一组两个方法 get 和 set 一样。

您当然可以将事件更改为“原始”委托(delegate):

public EventHandler<EventArgs> OnSomething;

这个你可以通过引用传递:

public bool TryRegisterAndStartTask(CustomTask task, ref EventHandler<EventArgs> del, EventHandler<EventArgs> sub, out Guid taskId) {
taskId = Guid.Empty;
// subscribe
del += sub;
...
}

CustomTaskManager.Instance.TryRegisterAndStartTask(task, ref task.OnSomething, OnSomethingWasRaised, out var taskId);

但这通常不是一个好主意,因为您正在失去事件的私有(private)范围 - 对于事件,您只能添加\删除委托(delegate),对于原始委托(delegate),任何人都可以做任何事情,例如调用或设置为 null。

如果保留常规事件 - 这意味着反射是实现目标的唯一方法,甚至更糟 - 你将不得不通过字符串名称而不是实际引用来引用你想要订阅的事件,尽管你可以使用nameof(task.OnSomething) .然后,您将失去订阅委托(delegate)类型的编译时验证。假设您想订阅 event Action Something但通过 Func<string>代表那里。它将通过反射方法编译得很好,并且只在运行时失败。

如果你坚持认为它看起来像这样:

public bool TryRegisterAndStartTask(CustomTask task, string eventName, Delegate sub, out Guid taskId) {
taskId = Guid.Empty;
// subscribe
var ev = task.GetType().GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance);
var addMethod = ev.GetAddMethod(); // this can be null or private by the way
addMethod.Invoke(task, new [] {sub});
...
}

然后这样调用:

var task = new CustomTaskA();
EventHandler<EventArgs> handler = OnSomethingWasRaised;
CustomTaskManager.Instance.TryRegisterAndStartTask(task, nameof(task.OnSomething), handler, out var taskId);

在我看来,在您的场景中丑陋、不安全且不值得。

关于c# - 根据规范监听事件并调用回调?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64719833/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com