Ho risolto questo problema di sincronizzazione per BeginInvoke utilizzando il suggerimento di Hans Passant per rilevare ObjectDisposedException. Finora, sembra funzionare. Ho creato metodi di estensione della classe Control per facilitare questo.
TryBeginInvoke tenta di richiamare il proprio metodo sul controllo. Se il metodo viene richiamato correttamente, controlla se il controllo è stato eliminato. Se è stato smaltito, ritorna immediatamente; in caso contrario, chiama il metodo originariamente passato come parametro a TryBeginInvoke.Il codice è il seguente:
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);
}
Ciò potrebbe almeno attenuarlo. Il mio codice come scritto non richiede Invoke? Queste eccezioni vengono lanciate circa 1 su 10 volte. Posso testarli! :) – drifter
Non è necessario, non è necessario ritardare la discussione e attendere che il delegato termini l'esecuzione. Non stai facendo nulla * dopo * la chiamata Invoke. Non puoi testarlo davvero una volta che usi BeginInvoke, dovrai aprire e chiudere il tuo modulo almeno un milione di volte. Ti assicuriamo che la gara è ancora lì, dovrai prendere ODE. –
Non mi dispiacerebbe prendere l'ObjectDisposedException, ma a volte getta InvalidOperationException invece ("Invoke o BeginInvoke non può essere chiamato su un controllo finché non viene creato l'handle della finestra.") Non riesco a capirlo a meno che non riesca a distinguerlo dagli altri InvalidOperationException nel codice invocato, giusto? – drifter