Removing a Delegate from an Event Inside a Task's Continuation Method
-
2012年3月13日 14:44
I have a 'stop process button' `ToolStripButton` on a parent MDI form. From a child form I start a process on a background thread using TPL, and get the tasks return status and changes the UI accordingly. The `Click` event of the 'stop process button' is dealt with using a `delegate` which works well. However, I cannot seem to remove it. Please see the code below:
private void buttonRunValid_Click(object sender, EventArgs e)
{
// UI.
mainForm.stopButton.Enabled = true;
// Thread cancellation.
cancelSource = new CancellationTokenSource();
token = cancelSource.Token;// Start new parallel task and pass uiScheduler.
Task<bool> asyncValidationTask = new Task<bool>(state =>
asyncRunValidationProcess(uiScheduler, token, ref timeSpan),
"Running Validation Process");
asyncValidationTask.Start();
// Callback for cancellation.
asyncValidationTask.ContinueWith(task =>
{
// Do some error checking with task.Status...// Remove delegate (back on uiThread).
mainForm.stopButton.Click -= delegate
{
UtilsTPL.CancelRunningProcess(ref mainForm, asyncValidationTask, cancelSource);
};
return;
}, TaskScheduler.FromCurrentSynchronizationContext());// Handle the cancellation.
mainForm.stopButton.Click += delegate
{
UtilsTPL.CancelRunningProcess(ref mainForm, asyncValidationTask, cancelSource);
};
return;
}where `mainForm` is the `MdiParent` and `stopButton` is an accessor to the `mainForm`'s `ToolStripButton` which fires the cancel/stop process event.
I am attempting to remove the `delegate` in the tasks continuation method but this is not working. I have also tried to loop through all events contained by the `ToolStripButton` using reflection:
FieldInfo fieldInfo =
typeof(Control).GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic);
object obj = fieldInfo.GetValue(_stripButton);
PropertyInfo propertyInfo =
_stripButton.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)propertyInfo.GetValue(_stripButton, null);
list.RemoveHandler(obj, list[obj]);This also does not work. How can I remove the `EventHandler`/`delegate`?
"Everything should be made as simple as possible, but not simpler" - Einstein
全部回复
-
2012年3月13日 15:05Your code creates two anonymous functions F1 and F2 which happen to have the same code. When you try removing F1 from the event, no delegate to that method is found because it's F2 you have added. Create a named method.
- 已标记为答案 Camuvingian 2012年3月13日 15:33
- 取消答案标记 Camuvingian 2012年3月13日 19:59
-
2012年3月13日 16:35
I have replaced one anonomous type with another. This clearly does not solve the issue:
mainForm.stopButton.Click -= delegate { ConstantCancel(asyncValidationTask); };
where:private void ConstantCancel(Task _task) { UtilsTPL.CancelRunningProcess(ref mainForm, _task, cancelSource); }
Do you mean use and EventHandler in place of the delegate?"Everything should be made as simple as possible, but not simpler" - Einstein
-
2012年3月14日 11:16
You're still creating anonymous functions. You changed their bodies to call another method, but they are still anonymous and distinct. The problem you have is apparently the asyncValidationTask argument. Create a class with a field to put the parameter in and a method which will read that field. Instantiate the class, fill its field and use its method as event handler.
-
2012年3月14日 12:07
Forget about creating a new class. What you need is to store the delegate in a variable and use that variable both when you add the event handler and when you remove it.private void buttonRunValid_Click(object sender, EventArgs e) { // UI. mainForm.stopButton.Enabled = true; // Thread cancellation. cancelSource = new CancellationTokenSource(); token = cancelSource.Token; // Start new parallel task and pass uiScheduler. Task<bool> asyncValidationTask = new Task<bool>(state => asyncRunValidationProcess(uiScheduler, token, ref timeSpan), "Running Validation Process"); asyncValidationTask.Start(); EventHandler cancelHandler = delegate { UtilsTPL.CancelRunningProcess(ref mainForm, asyncValidationTask, cancelSource); }; // Callback for cancellation. asyncValidationTask.ContinueWith(task => { // Do some error checking with task.Status... // Remove delegate (back on uiThread). mainForm.stopButton.Click -= cancelHandler; return; }, TaskScheduler.FromCurrentSynchronizationContext()); // Handle the cancellation. mainForm.stopButton.Click += cancelHandler; return; }- 已标记为答案 Camuvingian 2012年3月14日 12:10

