Я решил эту проблему синхронизации для BeginInvoke, используя рекомендацию Hans Passant, чтобы поймать ObjectDisposedException. Пока это работает. Для облегчения этого я создал методы расширения класса Control.
TryBeginInvoke пытается вызвать свой собственный метод управления. Если метод успешно вызван, он проверяет, был ли установлен элемент управления. Если он был удален, он немедленно возвращается; в противном случае он вызывает метод, первоначально переданный как параметр TryBeginInvoke.Код выглядит следующим образом:
public static class ControlExtension
{
// --- Static Fields ---
static bool _fieldsInitialized = false;
static InvokeDelegateDelegate _methodInvokeDelegate; // Initialized lazily to reduce application startup overhead [see method: InitStaticFields]
static InvokeMethodDelegate _methodInvokeMethod; // Initialized lazily to reduce application startup overhead [see method: InitStaticFields]
// --- Public Static Methods ---
public static bool TryBeginInvoke(this Control control, Delegate method, params object[] args)
{
IAsyncResult asyncResult;
return TryBeginInvoke(control, method, out asyncResult, args);
}
/// <remarks>May return true even if the target of the invocation cannot execute due to being disposed during invocation.</remarks>
public static bool TryBeginInvoke(this Control control, Delegate method, out IAsyncResult asyncResult, params object[] args)
{
if (!_fieldsInitialized)
InitStaticFields();
asyncResult = null;
if (!control.IsHandleCreated || control.IsDisposed)
return false;
try
{
control.BeginInvoke(_methodInvokeDelegate, control, method, args);
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException) // Handle not created
{
return false;
}
return true;
}
public static bool TryBeginInvoke(this Control control, MethodInvoker method)
{
IAsyncResult asyncResult;
return TryBeginInvoke(control, method, out asyncResult);
}
/// <remarks>May return true even if the target of the invocation cannot execute due to being disposed during invocation.</remarks>
public static bool TryBeginInvoke(this Control control, MethodInvoker method, out IAsyncResult asyncResult)
{
if (!_fieldsInitialized)
InitStaticFields();
asyncResult = null;
if (!control.IsHandleCreated || control.IsDisposed)
return false;
try
{
control.BeginInvoke(_methodInvokeMethod, control, method);
}
catch (ObjectDisposedException)
{
return false;
}
catch (InvalidOperationException) // Handle not created
{
return false;
}
return true;
}
// --- Private Static Methods ---
private static void InitStaticFields()
{
_methodInvokeDelegate = new InvokeDelegateDelegate(InvokeDelegate);
_methodInvokeMethod = new InvokeMethodDelegate(InvokeMethod);
}
private static object InvokeDelegate(Control control, Delegate method, object[] args)
{
if (!control.IsHandleCreated || control.IsDisposed)
return null;
return method.DynamicInvoke(args);
}
private static void InvokeMethod(Control control, MethodInvoker method)
{
if (!control.IsHandleCreated || control.IsDisposed)
return;
method();
}
// --- Private Nested Types ---
delegate object InvokeDelegateDelegate(Control control, Delegate method, object[] args);
delegate void InvokeMethodDelegate(Control control, MethodInvoker method);
}
Это может по крайней мере смягчить его. Мой код в письменном виде не требует Invoke? Эти исключения выбрасываются примерно 1 из 10 раз. Я могу проверить их! :) – drifter
Это не так, не нужно задерживать поток и ждать завершения работы делегата. Вы ничего не делаете * после вызова Invoke. Вы не можете проверить его, как только вы используете BeginInvoke, вам нужно будет открыть и закрыть форму не менее миллиона раз. Будьте уверены, что гонка все еще там, вам нужно поймать ODE. –
Я бы не прочь поймать ObjectDisposedException, но иногда он выдает InvalidOperationException вместо этого («Invoke или BeginInvoke не может быть вызван в элементе управления до тех пор, пока дескриптор окна не будет создан».) Я не могу это понять, если не могу отличить его от других InvalidOperationException в вызываемом коде, не так ли? – drifter