Invoke呼び出しの匿名メソッド
-
05-07-2019 - |
質問
Control.Invoke内でデリゲートを匿名で呼び出したい構文に少し問題があります。
私たちは多くの異なるアプローチを試みましたが、すべて無駄になりました。
例:
myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); });
someParameterはこのメソッドに対してローカルです
上記の結果、コンパイラエラーが発生します。
デリゲート型ではないため、匿名メソッドを 'System.Delegate'型に変換できません
解決
Invoke
/ BeginInvoke
は(型指定されたデリゲートではなく) Delegate
を受け入れるため、作成するデリゲートのタイプをコンパイラに伝える必要があります; MethodInvoker
(2.0)または Action
(3.5)は一般的な選択肢です(同じ署名を持っていることに注意してください)。そのように:
control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});
パラメータを渡す必要がある場合は、「キャプチャされた変数」方法:
string message = "Hi";
control.Invoke((MethodInvoker) delegate {this.Text = message;});
(注意:キャプチャ async を使用する場合は少し注意する必要がありますが、 sync は問題ありません-つまり上記は問題ありません)
別のオプションは、拡張メソッドを記述することです:
public static void Invoke(this Control control, Action action)
{
control.Invoke((Delegate)action);
}
then:
this.Invoke(delegate { this.Text = "hi"; });
// or since we are using C# 3.0
this.Invoke(() => { this.Text = "hi"; });
もちろん、 BeginInvoke
でも同じことができます:
public static void BeginInvoke(this Control control, Action action)
{
control.BeginInvoke((Delegate)action);
}
C#3.0を使用できない場合は、おそらく Form
基本クラスで通常のインスタンスメソッドを使用しても同じことができます。
他のヒント
実際には、デリゲートキーワードを使用する必要はありません。ラムダをパラメーターとして渡すだけです:
control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
myControl.Invoke(new MethodInvoker(delegate() {...}))
デリゲートタイプを作成する必要があります。匿名メソッド作成のキーワード「デリゲート」は、少し誤解を招く恐れがあります。匿名デリゲートではなく、匿名メソッドを作成しています。作成したメソッドは、デリゲートで使用できます。このように:
myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
完全を期すために、これはActionメソッド/匿名メソッドの組み合わせでも実現できます。
//Process is a method, invoked as a method group
Dispatcher.Current.BeginInvoke((Action) Process);
//or use an anonymous method
Dispatcher.Current.BeginInvoke((Action)delegate => {
SomeFunc();
SomeOtherFunc();
});
メソッドから値を返すことがあるため、他の提案に問題がありました。 MethodInvokerを戻り値で使用しようとすると、気に入らないようです。したがって、私が使用する解決策は次のようなものです(これをより簡潔にする方法を聞いて非常に満足しています-c#.net 2.0を使用しています):
// Create delegates for the different return types needed.
private delegate void VoidDelegate();
private delegate Boolean ReturnBooleanDelegate();
private delegate Hashtable ReturnHashtableDelegate();
// Now use the delegates and the delegate() keyword to create
// an anonymous method as required
// Here a case where there's no value returned:
public void SetTitle(string title)
{
myWindow.Invoke(new VoidDelegate(delegate()
{
myWindow.Text = title;
}));
}
// Here's an example of a value being returned
public Hashtable CurrentlyLoadedDocs()
{
return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
{
return myWindow.CurrentlyLoadedDocs;
}));
}
なぜこれがコンパイラに違いをもたらすのか理解できませんでしたが、これで十分です。
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
control.Invoke(action);
}
}
ボーナス:エラー処理を追加します。これは、バックグラウンドスレッドから Control.Invoke
を使用している場合、コントロールのテキスト/進行状況/有効化状態を更新していないためです。コントロールが既に破棄されているかどうかに注意してください。
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
try
{
if (!control.IsDisposed) control.Invoke(action);
}
catch (ObjectDisposedException) { }
}
}